From 1176e44f889e3f5d045876e4688d80fef1750f37 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Sun, 10 Oct 2021 16:10:32 +0200 Subject: [PATCH 01/80] RISC-V: Add StarFive SoC Kconfig option commit 3d24568b01c5a7a9e88f73f917477b60edb35bfe upstream. Add StarFive Kconfig option to select SoC specific and common drivers required for these SoCs. Select subsystems required to boot so the required drivers gets enabled by default. Reviewed-by: Geert Uytterhoeven Acked-by: Palmer Dabbelt Signed-off-by: Emil Renner Berthing --- arch/riscv/Kconfig.socs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/riscv/Kconfig.socs b/arch/riscv/Kconfig.socs index 30676ebb16ebd..6ec44a22278a4 100644 --- a/arch/riscv/Kconfig.socs +++ b/arch/riscv/Kconfig.socs @@ -19,6 +19,14 @@ config SOC_SIFIVE help This enables support for SiFive SoC platform hardware. +config SOC_STARFIVE + bool "StarFive SoCs" + select PINCTRL + select RESET_CONTROLLER + select SIFIVE_PLIC + help + This enables support for StarFive SoC platform hardware. + config SOC_VIRT bool "QEMU Virt Machine" select CLINT_TIMER if RISCV_M_MODE From 2d71a39804864ada2d86f26afa72963692e31882 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Sun, 10 Oct 2021 16:48:27 +0200 Subject: [PATCH 02/80] dt-bindings: timer: Add StarFive JH7100 clint commit 3234d3a1374308615c0cde5e83e52f6b644eaf53 upstream. Add compatible string for the StarFive JH7100 clint. Reviewed-by: Geert Uytterhoeven Acked-by: Rob Herring Signed-off-by: Emil Renner Berthing --- Documentation/devicetree/bindings/timer/sifive,clint.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/timer/sifive,clint.yaml b/Documentation/devicetree/bindings/timer/sifive,clint.yaml index a35952f487426..8d5f4687add9e 100644 --- a/Documentation/devicetree/bindings/timer/sifive,clint.yaml +++ b/Documentation/devicetree/bindings/timer/sifive,clint.yaml @@ -25,6 +25,7 @@ properties: items: - enum: - sifive,fu540-c000-clint + - starfive,jh7100-clint - canaan,k210-clint - const: sifive,clint0 From cfb967049c1900722823fe173819134ed8aa140f Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Sun, 10 Oct 2021 16:48:27 +0200 Subject: [PATCH 03/80] dt-bindings: interrupt-controller: Add StarFive JH7100 plic commit 9ac16169b4d4359d3832669bf06aab9e51184828 upstream. Add compatible string for StarFive JH7100 plic. Reviewed-by: Geert Uytterhoeven Acked-by: Rob Herring Signed-off-by: Emil Renner Berthing --- .../bindings/interrupt-controller/sifive,plic-1.0.0.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/interrupt-controller/sifive,plic-1.0.0.yaml b/Documentation/devicetree/bindings/interrupt-controller/sifive,plic-1.0.0.yaml index 08d5a57ce00ff..28b6b17fe4b26 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/sifive,plic-1.0.0.yaml +++ b/Documentation/devicetree/bindings/interrupt-controller/sifive,plic-1.0.0.yaml @@ -45,6 +45,7 @@ properties: items: - enum: - sifive,fu540-c000-plic + - starfive,jh7100-plic - canaan,k210-plic - const: sifive,plic-1.0.0 From 845aa480f32fd50fcfb80d30d7d8ea4d2c9f2cfd Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 25 Jun 2021 11:29:51 +0200 Subject: [PATCH 04/80] dt-bindings: clock: starfive: Add JH7100 clock definitions commit 38bb8a7264daf0ff5bb3024ae94bc465de78203d upstream. Add all clock outputs for the StarFive JH7100 clock generator. Based on work by Ahmad Fatoum for Barebox, with "JH7100_" prefixes added to all definitions. Acked-by: Rob Herring Acked-by: Stephen Boyd Signed-off-by: Geert Uytterhoeven Signed-off-by: Emil Renner Berthing --- include/dt-bindings/clock/starfive-jh7100.h | 202 ++++++++++++++++++++ 1 file changed, 202 insertions(+) create mode 100644 include/dt-bindings/clock/starfive-jh7100.h diff --git a/include/dt-bindings/clock/starfive-jh7100.h b/include/dt-bindings/clock/starfive-jh7100.h new file mode 100644 index 0000000000000..aa0863b9728d9 --- /dev/null +++ b/include/dt-bindings/clock/starfive-jh7100.h @@ -0,0 +1,202 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/* + * Copyright (C) 2021 Ahmad Fatoum, Pengutronix + */ + +#ifndef __DT_BINDINGS_CLOCK_STARFIVE_JH7100_H__ +#define __DT_BINDINGS_CLOCK_STARFIVE_JH7100_H__ + +#define JH7100_CLK_CPUNDBUS_ROOT 0 +#define JH7100_CLK_DLA_ROOT 1 +#define JH7100_CLK_DSP_ROOT 2 +#define JH7100_CLK_GMACUSB_ROOT 3 +#define JH7100_CLK_PERH0_ROOT 4 +#define JH7100_CLK_PERH1_ROOT 5 +#define JH7100_CLK_VIN_ROOT 6 +#define JH7100_CLK_VOUT_ROOT 7 +#define JH7100_CLK_AUDIO_ROOT 8 +#define JH7100_CLK_CDECHIFI4_ROOT 9 +#define JH7100_CLK_CDEC_ROOT 10 +#define JH7100_CLK_VOUTBUS_ROOT 11 +#define JH7100_CLK_CPUNBUS_ROOT_DIV 12 +#define JH7100_CLK_DSP_ROOT_DIV 13 +#define JH7100_CLK_PERH0_SRC 14 +#define JH7100_CLK_PERH1_SRC 15 +#define JH7100_CLK_PLL0_TESTOUT 16 +#define JH7100_CLK_PLL1_TESTOUT 17 +#define JH7100_CLK_PLL2_TESTOUT 18 +#define JH7100_CLK_PLL2_REF 19 +#define JH7100_CLK_CPU_CORE 20 +#define JH7100_CLK_CPU_AXI 21 +#define JH7100_CLK_AHB_BUS 22 +#define JH7100_CLK_APB1_BUS 23 +#define JH7100_CLK_APB2_BUS 24 +#define JH7100_CLK_DOM3AHB_BUS 25 +#define JH7100_CLK_DOM7AHB_BUS 26 +#define JH7100_CLK_U74_CORE0 27 +#define JH7100_CLK_U74_CORE1 28 +#define JH7100_CLK_U74_AXI 29 +#define JH7100_CLK_U74RTC_TOGGLE 30 +#define JH7100_CLK_SGDMA2P_AXI 31 +#define JH7100_CLK_DMA2PNOC_AXI 32 +#define JH7100_CLK_SGDMA2P_AHB 33 +#define JH7100_CLK_DLA_BUS 34 +#define JH7100_CLK_DLA_AXI 35 +#define JH7100_CLK_DLANOC_AXI 36 +#define JH7100_CLK_DLA_APB 37 +#define JH7100_CLK_VP6_CORE 38 +#define JH7100_CLK_VP6BUS_SRC 39 +#define JH7100_CLK_VP6_AXI 40 +#define JH7100_CLK_VCDECBUS_SRC 41 +#define JH7100_CLK_VDEC_BUS 42 +#define JH7100_CLK_VDEC_AXI 43 +#define JH7100_CLK_VDECBRG_MAIN 44 +#define JH7100_CLK_VDEC_BCLK 45 +#define JH7100_CLK_VDEC_CCLK 46 +#define JH7100_CLK_VDEC_APB 47 +#define JH7100_CLK_JPEG_AXI 48 +#define JH7100_CLK_JPEG_CCLK 49 +#define JH7100_CLK_JPEG_APB 50 +#define JH7100_CLK_GC300_2X 51 +#define JH7100_CLK_GC300_AHB 52 +#define JH7100_CLK_JPCGC300_AXIBUS 53 +#define JH7100_CLK_GC300_AXI 54 +#define JH7100_CLK_JPCGC300_MAIN 55 +#define JH7100_CLK_VENC_BUS 56 +#define JH7100_CLK_VENC_AXI 57 +#define JH7100_CLK_VENCBRG_MAIN 58 +#define JH7100_CLK_VENC_BCLK 59 +#define JH7100_CLK_VENC_CCLK 60 +#define JH7100_CLK_VENC_APB 61 +#define JH7100_CLK_DDRPLL_DIV2 62 +#define JH7100_CLK_DDRPLL_DIV4 63 +#define JH7100_CLK_DDRPLL_DIV8 64 +#define JH7100_CLK_DDROSC_DIV2 65 +#define JH7100_CLK_DDRC0 66 +#define JH7100_CLK_DDRC1 67 +#define JH7100_CLK_DDRPHY_APB 68 +#define JH7100_CLK_NOC_ROB 69 +#define JH7100_CLK_NOC_COG 70 +#define JH7100_CLK_NNE_AHB 71 +#define JH7100_CLK_NNEBUS_SRC1 72 +#define JH7100_CLK_NNE_BUS 73 +#define JH7100_CLK_NNE_AXI 74 +#define JH7100_CLK_NNENOC_AXI 75 +#define JH7100_CLK_DLASLV_AXI 76 +#define JH7100_CLK_DSPX2C_AXI 77 +#define JH7100_CLK_HIFI4_SRC 78 +#define JH7100_CLK_HIFI4_COREFREE 79 +#define JH7100_CLK_HIFI4_CORE 80 +#define JH7100_CLK_HIFI4_BUS 81 +#define JH7100_CLK_HIFI4_AXI 82 +#define JH7100_CLK_HIFI4NOC_AXI 83 +#define JH7100_CLK_SGDMA1P_BUS 84 +#define JH7100_CLK_SGDMA1P_AXI 85 +#define JH7100_CLK_DMA1P_AXI 86 +#define JH7100_CLK_X2C_AXI 87 +#define JH7100_CLK_USB_BUS 88 +#define JH7100_CLK_USB_AXI 89 +#define JH7100_CLK_USBNOC_AXI 90 +#define JH7100_CLK_USBPHY_ROOTDIV 91 +#define JH7100_CLK_USBPHY_125M 92 +#define JH7100_CLK_USBPHY_PLLDIV25M 93 +#define JH7100_CLK_USBPHY_25M 94 +#define JH7100_CLK_AUDIO_DIV 95 +#define JH7100_CLK_AUDIO_SRC 96 +#define JH7100_CLK_AUDIO_12288 97 +#define JH7100_CLK_VIN_SRC 98 +#define JH7100_CLK_ISP0_BUS 99 +#define JH7100_CLK_ISP0_AXI 100 +#define JH7100_CLK_ISP0NOC_AXI 101 +#define JH7100_CLK_ISPSLV_AXI 102 +#define JH7100_CLK_ISP1_BUS 103 +#define JH7100_CLK_ISP1_AXI 104 +#define JH7100_CLK_ISP1NOC_AXI 105 +#define JH7100_CLK_VIN_BUS 106 +#define JH7100_CLK_VIN_AXI 107 +#define JH7100_CLK_VINNOC_AXI 108 +#define JH7100_CLK_VOUT_SRC 109 +#define JH7100_CLK_DISPBUS_SRC 110 +#define JH7100_CLK_DISP_BUS 111 +#define JH7100_CLK_DISP_AXI 112 +#define JH7100_CLK_DISPNOC_AXI 113 +#define JH7100_CLK_SDIO0_AHB 114 +#define JH7100_CLK_SDIO0_CCLKINT 115 +#define JH7100_CLK_SDIO0_CCLKINT_INV 116 +#define JH7100_CLK_SDIO1_AHB 117 +#define JH7100_CLK_SDIO1_CCLKINT 118 +#define JH7100_CLK_SDIO1_CCLKINT_INV 119 +#define JH7100_CLK_GMAC_AHB 120 +#define JH7100_CLK_GMAC_ROOT_DIV 121 +#define JH7100_CLK_GMAC_PTP_REF 122 +#define JH7100_CLK_GMAC_GTX 123 +#define JH7100_CLK_GMAC_RMII_TX 124 +#define JH7100_CLK_GMAC_RMII_RX 125 +#define JH7100_CLK_GMAC_TX 126 +#define JH7100_CLK_GMAC_TX_INV 127 +#define JH7100_CLK_GMAC_RX_PRE 128 +#define JH7100_CLK_GMAC_RX_INV 129 +#define JH7100_CLK_GMAC_RMII 130 +#define JH7100_CLK_GMAC_TOPHYREF 131 +#define JH7100_CLK_SPI2AHB_AHB 132 +#define JH7100_CLK_SPI2AHB_CORE 133 +#define JH7100_CLK_EZMASTER_AHB 134 +#define JH7100_CLK_E24_AHB 135 +#define JH7100_CLK_E24RTC_TOGGLE 136 +#define JH7100_CLK_QSPI_AHB 137 +#define JH7100_CLK_QSPI_APB 138 +#define JH7100_CLK_QSPI_REF 139 +#define JH7100_CLK_SEC_AHB 140 +#define JH7100_CLK_AES 141 +#define JH7100_CLK_SHA 142 +#define JH7100_CLK_PKA 143 +#define JH7100_CLK_TRNG_APB 144 +#define JH7100_CLK_OTP_APB 145 +#define JH7100_CLK_UART0_APB 146 +#define JH7100_CLK_UART0_CORE 147 +#define JH7100_CLK_UART1_APB 148 +#define JH7100_CLK_UART1_CORE 149 +#define JH7100_CLK_SPI0_APB 150 +#define JH7100_CLK_SPI0_CORE 151 +#define JH7100_CLK_SPI1_APB 152 +#define JH7100_CLK_SPI1_CORE 153 +#define JH7100_CLK_I2C0_APB 154 +#define JH7100_CLK_I2C0_CORE 155 +#define JH7100_CLK_I2C1_APB 156 +#define JH7100_CLK_I2C1_CORE 157 +#define JH7100_CLK_GPIO_APB 158 +#define JH7100_CLK_UART2_APB 159 +#define JH7100_CLK_UART2_CORE 160 +#define JH7100_CLK_UART3_APB 161 +#define JH7100_CLK_UART3_CORE 162 +#define JH7100_CLK_SPI2_APB 163 +#define JH7100_CLK_SPI2_CORE 164 +#define JH7100_CLK_SPI3_APB 165 +#define JH7100_CLK_SPI3_CORE 166 +#define JH7100_CLK_I2C2_APB 167 +#define JH7100_CLK_I2C2_CORE 168 +#define JH7100_CLK_I2C3_APB 169 +#define JH7100_CLK_I2C3_CORE 170 +#define JH7100_CLK_WDTIMER_APB 171 +#define JH7100_CLK_WDT_CORE 172 +#define JH7100_CLK_TIMER0_CORE 173 +#define JH7100_CLK_TIMER1_CORE 174 +#define JH7100_CLK_TIMER2_CORE 175 +#define JH7100_CLK_TIMER3_CORE 176 +#define JH7100_CLK_TIMER4_CORE 177 +#define JH7100_CLK_TIMER5_CORE 178 +#define JH7100_CLK_TIMER6_CORE 179 +#define JH7100_CLK_VP6INTC_APB 180 +#define JH7100_CLK_PWM_APB 181 +#define JH7100_CLK_MSI_APB 182 +#define JH7100_CLK_TEMP_APB 183 +#define JH7100_CLK_TEMP_SENSE 184 +#define JH7100_CLK_SYSERR_APB 185 + +#define JH7100_CLK_PLL0_OUT 186 +#define JH7100_CLK_PLL1_OUT 187 +#define JH7100_CLK_PLL2_OUT 188 + +#define JH7100_CLK_END 189 + +#endif /* __DT_BINDINGS_CLOCK_STARFIVE_JH7100_H__ */ From 608ac45444d76c002534c1a6e9762209f093f320 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 1 Jun 2021 16:02:23 +0200 Subject: [PATCH 05/80] dt-bindings: clock: starfive: Add JH7100 bindings commit af35098f4fcd1f9bfc58dc37479e0786a4d85e96 upstream. Add bindings for the clock generator on the JH7100 RISC-V SoC by StarFive Ltd. This is a test chip for their upcoming JH7110 SoC. Reviewed-by: Rob Herring Acked-by: Stephen Boyd Signed-off-by: Geert Uytterhoeven Signed-off-by: Emil Renner Berthing --- .../clock/starfive,jh7100-clkgen.yaml | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/starfive,jh7100-clkgen.yaml diff --git a/Documentation/devicetree/bindings/clock/starfive,jh7100-clkgen.yaml b/Documentation/devicetree/bindings/clock/starfive,jh7100-clkgen.yaml new file mode 100644 index 0000000000000..12f17b60ecbeb --- /dev/null +++ b/Documentation/devicetree/bindings/clock/starfive,jh7100-clkgen.yaml @@ -0,0 +1,56 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/starfive,jh7100-clkgen.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive JH7100 Clock Generator + +maintainers: + - Geert Uytterhoeven + - Emil Renner Berthing + +properties: + compatible: + const: starfive,jh7100-clkgen + + reg: + maxItems: 1 + + clocks: + items: + - description: Main clock source (25 MHz) + - description: Application-specific clock source (12-27 MHz) + - description: RMII reference clock (50 MHz) + - description: RGMII RX clock (125 MHz) + + clock-names: + items: + - const: osc_sys + - const: osc_aud + - const: gmac_rmii_ref + - const: gmac_gr_mii_rxclk + + '#clock-cells': + const: 1 + description: + See for valid indices. + +required: + - compatible + - reg + - clocks + - clock-names + - '#clock-cells' + +additionalProperties: false + +examples: + - | + clock-controller@11800000 { + compatible = "starfive,jh7100-clkgen"; + reg = <0x11800000 0x10000>; + clocks = <&osc_sys>, <&osc_aud>, <&gmac_rmii_ref>, <&gmac_gr_mii_rxclk>; + clock-names = "osc_sys", "osc_aud", "gmac_rmii_ref", "gmac_gr_mii_rxclk"; + #clock-cells = <1>; + }; From fc4f8c10530c0ee94f93062ab5d9c2f0c93b83ab Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 1 Jun 2021 15:57:52 +0200 Subject: [PATCH 06/80] clk: starfive: Add JH7100 clock generator driver commit 4210be668a09ee20e4e1c7adf61b47d33d05c480 upstream. Add a driver for the StarFive JH7100 clock generator. Reviewed-by: Andy Shevchenko Acked-by: Stephen Boyd Signed-off-by: Geert Uytterhoeven Co-developed-by: Emil Renner Berthing Signed-off-by: Emil Renner Berthing --- MAINTAINERS | 7 + drivers/clk/Kconfig | 1 + drivers/clk/Makefile | 1 + drivers/clk/starfive/Kconfig | 9 + drivers/clk/starfive/Makefile | 3 + drivers/clk/starfive/clk-starfive-jh7100.c | 689 +++++++++++++++++++++ 6 files changed, 710 insertions(+) create mode 100644 drivers/clk/starfive/Kconfig create mode 100644 drivers/clk/starfive/Makefile create mode 100644 drivers/clk/starfive/clk-starfive-jh7100.c diff --git a/MAINTAINERS b/MAINTAINERS index fb18ce7168aa7..d974f4fb7b3a4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -18156,6 +18156,13 @@ M: Ion Badulescu S: Odd Fixes F: drivers/net/ethernet/adaptec/starfire* +STARFIVE JH7100 CLOCK DRIVER +M: Emil Renner Berthing +S: Maintained +F: Documentation/devicetree/bindings/clock/starfive,jh7100-clkgen.yaml +F: drivers/clk/starfive/clk-starfive-jh7100.c +F: include/dt-bindings/clock/starfive-jh7100.h + STATIC BRANCH/CALL M: Peter Zijlstra M: Josh Poimboeuf diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index c5b3dc97396a6..c91931c94888e 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -412,6 +412,7 @@ source "drivers/clk/samsung/Kconfig" source "drivers/clk/sifive/Kconfig" source "drivers/clk/socfpga/Kconfig" source "drivers/clk/sprd/Kconfig" +source "drivers/clk/starfive/Kconfig" source "drivers/clk/sunxi/Kconfig" source "drivers/clk/sunxi-ng/Kconfig" source "drivers/clk/tegra/Kconfig" diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index e42312121e511..a9bb2478fbddd 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -109,6 +109,7 @@ obj-y += socfpga/ obj-$(CONFIG_PLAT_SPEAR) += spear/ obj-y += sprd/ obj-$(CONFIG_ARCH_STI) += st/ +obj-$(CONFIG_SOC_STARFIVE) += starfive/ obj-$(CONFIG_ARCH_SUNXI) += sunxi/ obj-$(CONFIG_SUNXI_CCU) += sunxi-ng/ obj-$(CONFIG_ARCH_TEGRA) += tegra/ diff --git a/drivers/clk/starfive/Kconfig b/drivers/clk/starfive/Kconfig new file mode 100644 index 0000000000000..c0fa9d5e641fe --- /dev/null +++ b/drivers/clk/starfive/Kconfig @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0 + +config CLK_STARFIVE_JH7100 + bool "StarFive JH7100 clock support" + depends on SOC_STARFIVE || COMPILE_TEST + default SOC_STARFIVE + help + Say yes here to support the clock controller on the StarFive JH7100 + SoC. diff --git a/drivers/clk/starfive/Makefile b/drivers/clk/starfive/Makefile new file mode 100644 index 0000000000000..09759cc735307 --- /dev/null +++ b/drivers/clk/starfive/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 +# StarFive Clock +obj-$(CONFIG_CLK_STARFIVE_JH7100) += clk-starfive-jh7100.o diff --git a/drivers/clk/starfive/clk-starfive-jh7100.c b/drivers/clk/starfive/clk-starfive-jh7100.c new file mode 100644 index 0000000000000..25d31afa0f871 --- /dev/null +++ b/drivers/clk/starfive/clk-starfive-jh7100.c @@ -0,0 +1,689 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * StarFive JH7100 Clock Generator Driver + * + * Copyright 2021 Ahmad Fatoum, Pengutronix + * Copyright (C) 2021 Glider bv + * Copyright (C) 2021 Emil Renner Berthing + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* external clocks */ +#define JH7100_CLK_OSC_SYS (JH7100_CLK_END + 0) +#define JH7100_CLK_OSC_AUD (JH7100_CLK_END + 1) +#define JH7100_CLK_GMAC_RMII_REF (JH7100_CLK_END + 2) +#define JH7100_CLK_GMAC_GR_MII_RX (JH7100_CLK_END + 3) + +/* register fields */ +#define JH7100_CLK_ENABLE BIT(31) +#define JH7100_CLK_INVERT BIT(30) +#define JH7100_CLK_MUX_MASK GENMASK(27, 24) +#define JH7100_CLK_MUX_SHIFT 24 +#define JH7100_CLK_DIV_MASK GENMASK(23, 0) + +/* clock data */ +#define JH7100_GATE(_idx, _name, _flags, _parent) [_idx] = { \ + .name = _name, \ + .flags = CLK_SET_RATE_PARENT | (_flags), \ + .max = JH7100_CLK_ENABLE, \ + .parents = { [0] = _parent }, \ +} + +#define JH7100__DIV(_idx, _name, _max, _parent) [_idx] = { \ + .name = _name, \ + .flags = 0, \ + .max = _max, \ + .parents = { [0] = _parent }, \ +} + +#define JH7100_GDIV(_idx, _name, _flags, _max, _parent) [_idx] = { \ + .name = _name, \ + .flags = _flags, \ + .max = JH7100_CLK_ENABLE | (_max), \ + .parents = { [0] = _parent }, \ +} + +#define JH7100__MUX(_idx, _name, _nparents, ...) [_idx] = { \ + .name = _name, \ + .flags = 0, \ + .max = ((_nparents) - 1) << JH7100_CLK_MUX_SHIFT, \ + .parents = { __VA_ARGS__ }, \ +} + +#define JH7100_GMUX(_idx, _name, _flags, _nparents, ...) [_idx] = { \ + .name = _name, \ + .flags = _flags, \ + .max = JH7100_CLK_ENABLE | \ + (((_nparents) - 1) << JH7100_CLK_MUX_SHIFT), \ + .parents = { __VA_ARGS__ }, \ +} + +#define JH7100__INV(_idx, _name, _parent) [_idx] = { \ + .name = _name, \ + .flags = CLK_SET_RATE_PARENT, \ + .max = JH7100_CLK_INVERT, \ + .parents = { [0] = _parent }, \ +} + +static const struct { + const char *name; + unsigned long flags; + u32 max; + u8 parents[4]; +} jh7100_clk_data[] __initconst = { + JH7100__MUX(JH7100_CLK_CPUNDBUS_ROOT, "cpundbus_root", 4, + JH7100_CLK_OSC_SYS, + JH7100_CLK_PLL0_OUT, + JH7100_CLK_PLL1_OUT, + JH7100_CLK_PLL2_OUT), + JH7100__MUX(JH7100_CLK_DLA_ROOT, "dla_root", 3, + JH7100_CLK_OSC_SYS, + JH7100_CLK_PLL1_OUT, + JH7100_CLK_PLL2_OUT), + JH7100__MUX(JH7100_CLK_DSP_ROOT, "dsp_root", 4, + JH7100_CLK_OSC_SYS, + JH7100_CLK_PLL0_OUT, + JH7100_CLK_PLL1_OUT, + JH7100_CLK_PLL2_OUT), + JH7100__MUX(JH7100_CLK_GMACUSB_ROOT, "gmacusb_root", 3, + JH7100_CLK_OSC_SYS, + JH7100_CLK_PLL0_OUT, + JH7100_CLK_PLL2_OUT), + JH7100__MUX(JH7100_CLK_PERH0_ROOT, "perh0_root", 2, + JH7100_CLK_OSC_SYS, + JH7100_CLK_PLL0_OUT), + JH7100__MUX(JH7100_CLK_PERH1_ROOT, "perh1_root", 2, + JH7100_CLK_OSC_SYS, + JH7100_CLK_PLL2_OUT), + JH7100__MUX(JH7100_CLK_VIN_ROOT, "vin_root", 3, + JH7100_CLK_OSC_SYS, + JH7100_CLK_PLL1_OUT, + JH7100_CLK_PLL2_OUT), + JH7100__MUX(JH7100_CLK_VOUT_ROOT, "vout_root", 3, + JH7100_CLK_OSC_AUD, + JH7100_CLK_PLL0_OUT, + JH7100_CLK_PLL2_OUT), + JH7100_GDIV(JH7100_CLK_AUDIO_ROOT, "audio_root", 0, 8, JH7100_CLK_PLL0_OUT), + JH7100__MUX(JH7100_CLK_CDECHIFI4_ROOT, "cdechifi4_root", 3, + JH7100_CLK_OSC_SYS, + JH7100_CLK_PLL1_OUT, + JH7100_CLK_PLL2_OUT), + JH7100__MUX(JH7100_CLK_CDEC_ROOT, "cdec_root", 3, + JH7100_CLK_OSC_SYS, + JH7100_CLK_PLL0_OUT, + JH7100_CLK_PLL1_OUT), + JH7100__MUX(JH7100_CLK_VOUTBUS_ROOT, "voutbus_root", 3, + JH7100_CLK_OSC_AUD, + JH7100_CLK_PLL0_OUT, + JH7100_CLK_PLL2_OUT), + JH7100__DIV(JH7100_CLK_CPUNBUS_ROOT_DIV, "cpunbus_root_div", 2, JH7100_CLK_CPUNDBUS_ROOT), + JH7100__DIV(JH7100_CLK_DSP_ROOT_DIV, "dsp_root_div", 4, JH7100_CLK_DSP_ROOT), + JH7100__DIV(JH7100_CLK_PERH0_SRC, "perh0_src", 4, JH7100_CLK_PERH0_ROOT), + JH7100__DIV(JH7100_CLK_PERH1_SRC, "perh1_src", 4, JH7100_CLK_PERH1_ROOT), + JH7100_GDIV(JH7100_CLK_PLL0_TESTOUT, "pll0_testout", 0, 31, JH7100_CLK_PERH0_SRC), + JH7100_GDIV(JH7100_CLK_PLL1_TESTOUT, "pll1_testout", 0, 31, JH7100_CLK_DLA_ROOT), + JH7100_GDIV(JH7100_CLK_PLL2_TESTOUT, "pll2_testout", 0, 31, JH7100_CLK_PERH1_SRC), + JH7100__MUX(JH7100_CLK_PLL2_REF, "pll2_refclk", 2, + JH7100_CLK_OSC_SYS, + JH7100_CLK_OSC_AUD), + JH7100__DIV(JH7100_CLK_CPU_CORE, "cpu_core", 8, JH7100_CLK_CPUNBUS_ROOT_DIV), + JH7100__DIV(JH7100_CLK_CPU_AXI, "cpu_axi", 8, JH7100_CLK_CPU_CORE), + JH7100__DIV(JH7100_CLK_AHB_BUS, "ahb_bus", 8, JH7100_CLK_CPUNBUS_ROOT_DIV), + JH7100__DIV(JH7100_CLK_APB1_BUS, "apb1_bus", 8, JH7100_CLK_AHB_BUS), + JH7100__DIV(JH7100_CLK_APB2_BUS, "apb2_bus", 8, JH7100_CLK_AHB_BUS), + JH7100_GATE(JH7100_CLK_DOM3AHB_BUS, "dom3ahb_bus", CLK_IS_CRITICAL, JH7100_CLK_AHB_BUS), + JH7100_GATE(JH7100_CLK_DOM7AHB_BUS, "dom7ahb_bus", CLK_IS_CRITICAL, JH7100_CLK_AHB_BUS), + JH7100_GATE(JH7100_CLK_U74_CORE0, "u74_core0", CLK_IS_CRITICAL, JH7100_CLK_CPU_CORE), + JH7100_GDIV(JH7100_CLK_U74_CORE1, "u74_core1", CLK_IS_CRITICAL, 8, JH7100_CLK_CPU_CORE), + JH7100_GATE(JH7100_CLK_U74_AXI, "u74_axi", CLK_IS_CRITICAL, JH7100_CLK_CPU_AXI), + JH7100_GATE(JH7100_CLK_U74RTC_TOGGLE, "u74rtc_toggle", CLK_IS_CRITICAL, JH7100_CLK_OSC_SYS), + JH7100_GATE(JH7100_CLK_SGDMA2P_AXI, "sgdma2p_axi", 0, JH7100_CLK_CPU_AXI), + JH7100_GATE(JH7100_CLK_DMA2PNOC_AXI, "dma2pnoc_axi", 0, JH7100_CLK_CPU_AXI), + JH7100_GATE(JH7100_CLK_SGDMA2P_AHB, "sgdma2p_ahb", 0, JH7100_CLK_AHB_BUS), + JH7100__DIV(JH7100_CLK_DLA_BUS, "dla_bus", 4, JH7100_CLK_DLA_ROOT), + JH7100_GATE(JH7100_CLK_DLA_AXI, "dla_axi", 0, JH7100_CLK_DLA_BUS), + JH7100_GATE(JH7100_CLK_DLANOC_AXI, "dlanoc_axi", 0, JH7100_CLK_DLA_BUS), + JH7100_GATE(JH7100_CLK_DLA_APB, "dla_apb", 0, JH7100_CLK_APB1_BUS), + JH7100_GDIV(JH7100_CLK_VP6_CORE, "vp6_core", 0, 4, JH7100_CLK_DSP_ROOT_DIV), + JH7100__DIV(JH7100_CLK_VP6BUS_SRC, "vp6bus_src", 4, JH7100_CLK_DSP_ROOT), + JH7100_GDIV(JH7100_CLK_VP6_AXI, "vp6_axi", 0, 4, JH7100_CLK_VP6BUS_SRC), + JH7100__DIV(JH7100_CLK_VCDECBUS_SRC, "vcdecbus_src", 4, JH7100_CLK_CDECHIFI4_ROOT), + JH7100__DIV(JH7100_CLK_VDEC_BUS, "vdec_bus", 8, JH7100_CLK_VCDECBUS_SRC), + JH7100_GATE(JH7100_CLK_VDEC_AXI, "vdec_axi", 0, JH7100_CLK_VDEC_BUS), + JH7100_GATE(JH7100_CLK_VDECBRG_MAIN, "vdecbrg_mainclk", 0, JH7100_CLK_VDEC_BUS), + JH7100_GDIV(JH7100_CLK_VDEC_BCLK, "vdec_bclk", 0, 8, JH7100_CLK_VCDECBUS_SRC), + JH7100_GDIV(JH7100_CLK_VDEC_CCLK, "vdec_cclk", 0, 8, JH7100_CLK_CDEC_ROOT), + JH7100_GATE(JH7100_CLK_VDEC_APB, "vdec_apb", 0, JH7100_CLK_APB1_BUS), + JH7100_GDIV(JH7100_CLK_JPEG_AXI, "jpeg_axi", 0, 8, JH7100_CLK_CPUNBUS_ROOT_DIV), + JH7100_GDIV(JH7100_CLK_JPEG_CCLK, "jpeg_cclk", 0, 8, JH7100_CLK_CPUNBUS_ROOT_DIV), + JH7100_GATE(JH7100_CLK_JPEG_APB, "jpeg_apb", 0, JH7100_CLK_APB1_BUS), + JH7100_GDIV(JH7100_CLK_GC300_2X, "gc300_2x", 0, 8, JH7100_CLK_CDECHIFI4_ROOT), + JH7100_GATE(JH7100_CLK_GC300_AHB, "gc300_ahb", 0, JH7100_CLK_AHB_BUS), + JH7100__DIV(JH7100_CLK_JPCGC300_AXIBUS, "jpcgc300_axibus", 8, JH7100_CLK_VCDECBUS_SRC), + JH7100_GATE(JH7100_CLK_GC300_AXI, "gc300_axi", 0, JH7100_CLK_JPCGC300_AXIBUS), + JH7100_GATE(JH7100_CLK_JPCGC300_MAIN, "jpcgc300_mainclk", 0, JH7100_CLK_JPCGC300_AXIBUS), + JH7100__DIV(JH7100_CLK_VENC_BUS, "venc_bus", 8, JH7100_CLK_VCDECBUS_SRC), + JH7100_GATE(JH7100_CLK_VENC_AXI, "venc_axi", 0, JH7100_CLK_VENC_BUS), + JH7100_GATE(JH7100_CLK_VENCBRG_MAIN, "vencbrg_mainclk", 0, JH7100_CLK_VENC_BUS), + JH7100_GDIV(JH7100_CLK_VENC_BCLK, "venc_bclk", 0, 8, JH7100_CLK_VCDECBUS_SRC), + JH7100_GDIV(JH7100_CLK_VENC_CCLK, "venc_cclk", 0, 8, JH7100_CLK_CDEC_ROOT), + JH7100_GATE(JH7100_CLK_VENC_APB, "venc_apb", 0, JH7100_CLK_APB1_BUS), + JH7100_GDIV(JH7100_CLK_DDRPLL_DIV2, "ddrpll_div2", CLK_IS_CRITICAL, 2, JH7100_CLK_PLL1_OUT), + JH7100_GDIV(JH7100_CLK_DDRPLL_DIV4, "ddrpll_div4", CLK_IS_CRITICAL, 2, JH7100_CLK_DDRPLL_DIV2), + JH7100_GDIV(JH7100_CLK_DDRPLL_DIV8, "ddrpll_div8", CLK_IS_CRITICAL, 2, JH7100_CLK_DDRPLL_DIV4), + JH7100_GDIV(JH7100_CLK_DDROSC_DIV2, "ddrosc_div2", CLK_IS_CRITICAL, 2, JH7100_CLK_OSC_SYS), + JH7100_GMUX(JH7100_CLK_DDRC0, "ddrc0", CLK_IS_CRITICAL, 4, + JH7100_CLK_DDROSC_DIV2, + JH7100_CLK_DDRPLL_DIV2, + JH7100_CLK_DDRPLL_DIV4, + JH7100_CLK_DDRPLL_DIV8), + JH7100_GMUX(JH7100_CLK_DDRC1, "ddrc1", CLK_IS_CRITICAL, 4, + JH7100_CLK_DDROSC_DIV2, + JH7100_CLK_DDRPLL_DIV2, + JH7100_CLK_DDRPLL_DIV4, + JH7100_CLK_DDRPLL_DIV8), + JH7100_GATE(JH7100_CLK_DDRPHY_APB, "ddrphy_apb", 0, JH7100_CLK_APB1_BUS), + JH7100__DIV(JH7100_CLK_NOC_ROB, "noc_rob", 8, JH7100_CLK_CPUNBUS_ROOT_DIV), + JH7100__DIV(JH7100_CLK_NOC_COG, "noc_cog", 8, JH7100_CLK_DLA_ROOT), + JH7100_GATE(JH7100_CLK_NNE_AHB, "nne_ahb", 0, JH7100_CLK_AHB_BUS), + JH7100__DIV(JH7100_CLK_NNEBUS_SRC1, "nnebus_src1", 4, JH7100_CLK_DSP_ROOT), + JH7100__MUX(JH7100_CLK_NNE_BUS, "nne_bus", 2, + JH7100_CLK_CPU_AXI, + JH7100_CLK_NNEBUS_SRC1), + JH7100_GATE(JH7100_CLK_NNE_AXI, "nne_axi", 0, JH7100_CLK_NNE_BUS), + JH7100_GATE(JH7100_CLK_NNENOC_AXI, "nnenoc_axi", 0, JH7100_CLK_NNE_BUS), + JH7100_GATE(JH7100_CLK_DLASLV_AXI, "dlaslv_axi", 0, JH7100_CLK_NNE_BUS), + JH7100_GATE(JH7100_CLK_DSPX2C_AXI, "dspx2c_axi", CLK_IS_CRITICAL, JH7100_CLK_NNE_BUS), + JH7100__DIV(JH7100_CLK_HIFI4_SRC, "hifi4_src", 4, JH7100_CLK_CDECHIFI4_ROOT), + JH7100__DIV(JH7100_CLK_HIFI4_COREFREE, "hifi4_corefree", 8, JH7100_CLK_HIFI4_SRC), + JH7100_GATE(JH7100_CLK_HIFI4_CORE, "hifi4_core", 0, JH7100_CLK_HIFI4_COREFREE), + JH7100__DIV(JH7100_CLK_HIFI4_BUS, "hifi4_bus", 8, JH7100_CLK_HIFI4_COREFREE), + JH7100_GATE(JH7100_CLK_HIFI4_AXI, "hifi4_axi", 0, JH7100_CLK_HIFI4_BUS), + JH7100_GATE(JH7100_CLK_HIFI4NOC_AXI, "hifi4noc_axi", 0, JH7100_CLK_HIFI4_BUS), + JH7100__DIV(JH7100_CLK_SGDMA1P_BUS, "sgdma1p_bus", 8, JH7100_CLK_CPUNBUS_ROOT_DIV), + JH7100_GATE(JH7100_CLK_SGDMA1P_AXI, "sgdma1p_axi", 0, JH7100_CLK_SGDMA1P_BUS), + JH7100_GATE(JH7100_CLK_DMA1P_AXI, "dma1p_axi", 0, JH7100_CLK_SGDMA1P_BUS), + JH7100_GDIV(JH7100_CLK_X2C_AXI, "x2c_axi", CLK_IS_CRITICAL, 8, JH7100_CLK_CPUNBUS_ROOT_DIV), + JH7100__DIV(JH7100_CLK_USB_BUS, "usb_bus", 8, JH7100_CLK_CPUNBUS_ROOT_DIV), + JH7100_GATE(JH7100_CLK_USB_AXI, "usb_axi", 0, JH7100_CLK_USB_BUS), + JH7100_GATE(JH7100_CLK_USBNOC_AXI, "usbnoc_axi", 0, JH7100_CLK_USB_BUS), + JH7100__DIV(JH7100_CLK_USBPHY_ROOTDIV, "usbphy_rootdiv", 4, JH7100_CLK_GMACUSB_ROOT), + JH7100_GDIV(JH7100_CLK_USBPHY_125M, "usbphy_125m", 0, 8, JH7100_CLK_USBPHY_ROOTDIV), + JH7100_GDIV(JH7100_CLK_USBPHY_PLLDIV25M, "usbphy_plldiv25m", 0, 32, JH7100_CLK_USBPHY_ROOTDIV), + JH7100__MUX(JH7100_CLK_USBPHY_25M, "usbphy_25m", 2, + JH7100_CLK_OSC_SYS, + JH7100_CLK_USBPHY_PLLDIV25M), + JH7100__DIV(JH7100_CLK_AUDIO_DIV, "audio_div", 131072, JH7100_CLK_AUDIO_ROOT), + JH7100_GATE(JH7100_CLK_AUDIO_SRC, "audio_src", 0, JH7100_CLK_AUDIO_DIV), + JH7100_GATE(JH7100_CLK_AUDIO_12288, "audio_12288", 0, JH7100_CLK_OSC_AUD), + JH7100_GDIV(JH7100_CLK_VIN_SRC, "vin_src", 0, 4, JH7100_CLK_VIN_ROOT), + JH7100__DIV(JH7100_CLK_ISP0_BUS, "isp0_bus", 8, JH7100_CLK_VIN_SRC), + JH7100_GATE(JH7100_CLK_ISP0_AXI, "isp0_axi", 0, JH7100_CLK_ISP0_BUS), + JH7100_GATE(JH7100_CLK_ISP0NOC_AXI, "isp0noc_axi", 0, JH7100_CLK_ISP0_BUS), + JH7100_GATE(JH7100_CLK_ISPSLV_AXI, "ispslv_axi", 0, JH7100_CLK_ISP0_BUS), + JH7100__DIV(JH7100_CLK_ISP1_BUS, "isp1_bus", 8, JH7100_CLK_VIN_SRC), + JH7100_GATE(JH7100_CLK_ISP1_AXI, "isp1_axi", 0, JH7100_CLK_ISP1_BUS), + JH7100_GATE(JH7100_CLK_ISP1NOC_AXI, "isp1noc_axi", 0, JH7100_CLK_ISP1_BUS), + JH7100__DIV(JH7100_CLK_VIN_BUS, "vin_bus", 8, JH7100_CLK_VIN_SRC), + JH7100_GATE(JH7100_CLK_VIN_AXI, "vin_axi", 0, JH7100_CLK_VIN_BUS), + JH7100_GATE(JH7100_CLK_VINNOC_AXI, "vinnoc_axi", 0, JH7100_CLK_VIN_BUS), + JH7100_GDIV(JH7100_CLK_VOUT_SRC, "vout_src", 0, 4, JH7100_CLK_VOUT_ROOT), + JH7100__DIV(JH7100_CLK_DISPBUS_SRC, "dispbus_src", 4, JH7100_CLK_VOUTBUS_ROOT), + JH7100__DIV(JH7100_CLK_DISP_BUS, "disp_bus", 4, JH7100_CLK_DISPBUS_SRC), + JH7100_GATE(JH7100_CLK_DISP_AXI, "disp_axi", 0, JH7100_CLK_DISP_BUS), + JH7100_GATE(JH7100_CLK_DISPNOC_AXI, "dispnoc_axi", 0, JH7100_CLK_DISP_BUS), + JH7100_GATE(JH7100_CLK_SDIO0_AHB, "sdio0_ahb", 0, JH7100_CLK_AHB_BUS), + JH7100_GDIV(JH7100_CLK_SDIO0_CCLKINT, "sdio0_cclkint", 0, 24, JH7100_CLK_PERH0_SRC), + JH7100__INV(JH7100_CLK_SDIO0_CCLKINT_INV, "sdio0_cclkint_inv", JH7100_CLK_SDIO0_CCLKINT), + JH7100_GATE(JH7100_CLK_SDIO1_AHB, "sdio1_ahb", 0, JH7100_CLK_AHB_BUS), + JH7100_GDIV(JH7100_CLK_SDIO1_CCLKINT, "sdio1_cclkint", 0, 24, JH7100_CLK_PERH1_SRC), + JH7100__INV(JH7100_CLK_SDIO1_CCLKINT_INV, "sdio1_cclkint_inv", JH7100_CLK_SDIO1_CCLKINT), + JH7100_GATE(JH7100_CLK_GMAC_AHB, "gmac_ahb", 0, JH7100_CLK_AHB_BUS), + JH7100__DIV(JH7100_CLK_GMAC_ROOT_DIV, "gmac_root_div", 8, JH7100_CLK_GMACUSB_ROOT), + JH7100_GDIV(JH7100_CLK_GMAC_PTP_REF, "gmac_ptp_refclk", 0, 31, JH7100_CLK_GMAC_ROOT_DIV), + JH7100_GDIV(JH7100_CLK_GMAC_GTX, "gmac_gtxclk", 0, 255, JH7100_CLK_GMAC_ROOT_DIV), + JH7100_GDIV(JH7100_CLK_GMAC_RMII_TX, "gmac_rmii_txclk", 0, 8, JH7100_CLK_GMAC_RMII_REF), + JH7100_GDIV(JH7100_CLK_GMAC_RMII_RX, "gmac_rmii_rxclk", 0, 8, JH7100_CLK_GMAC_RMII_REF), + JH7100__MUX(JH7100_CLK_GMAC_TX, "gmac_tx", 3, + JH7100_CLK_GMAC_GTX, + JH7100_CLK_GMAC_TX_INV, + JH7100_CLK_GMAC_RMII_TX), + JH7100__INV(JH7100_CLK_GMAC_TX_INV, "gmac_tx_inv", JH7100_CLK_GMAC_TX), + JH7100__MUX(JH7100_CLK_GMAC_RX_PRE, "gmac_rx_pre", 2, + JH7100_CLK_GMAC_GR_MII_RX, + JH7100_CLK_GMAC_RMII_RX), + JH7100__INV(JH7100_CLK_GMAC_RX_INV, "gmac_rx_inv", JH7100_CLK_GMAC_RX_PRE), + JH7100_GATE(JH7100_CLK_GMAC_RMII, "gmac_rmii", 0, JH7100_CLK_GMAC_RMII_REF), + JH7100_GDIV(JH7100_CLK_GMAC_TOPHYREF, "gmac_tophyref", 0, 127, JH7100_CLK_GMAC_ROOT_DIV), + JH7100_GATE(JH7100_CLK_SPI2AHB_AHB, "spi2ahb_ahb", 0, JH7100_CLK_AHB_BUS), + JH7100_GDIV(JH7100_CLK_SPI2AHB_CORE, "spi2ahb_core", 0, 31, JH7100_CLK_PERH0_SRC), + JH7100_GATE(JH7100_CLK_EZMASTER_AHB, "ezmaster_ahb", 0, JH7100_CLK_AHB_BUS), + JH7100_GATE(JH7100_CLK_E24_AHB, "e24_ahb", 0, JH7100_CLK_AHB_BUS), + JH7100_GATE(JH7100_CLK_E24RTC_TOGGLE, "e24rtc_toggle", 0, JH7100_CLK_OSC_SYS), + JH7100_GATE(JH7100_CLK_QSPI_AHB, "qspi_ahb", 0, JH7100_CLK_AHB_BUS), + JH7100_GATE(JH7100_CLK_QSPI_APB, "qspi_apb", 0, JH7100_CLK_APB1_BUS), + JH7100_GDIV(JH7100_CLK_QSPI_REF, "qspi_refclk", 0, 31, JH7100_CLK_PERH0_SRC), + JH7100_GATE(JH7100_CLK_SEC_AHB, "sec_ahb", 0, JH7100_CLK_AHB_BUS), + JH7100_GATE(JH7100_CLK_AES, "aes_clk", 0, JH7100_CLK_SEC_AHB), + JH7100_GATE(JH7100_CLK_SHA, "sha_clk", 0, JH7100_CLK_SEC_AHB), + JH7100_GATE(JH7100_CLK_PKA, "pka_clk", 0, JH7100_CLK_SEC_AHB), + JH7100_GATE(JH7100_CLK_TRNG_APB, "trng_apb", 0, JH7100_CLK_APB1_BUS), + JH7100_GATE(JH7100_CLK_OTP_APB, "otp_apb", 0, JH7100_CLK_APB1_BUS), + JH7100_GATE(JH7100_CLK_UART0_APB, "uart0_apb", 0, JH7100_CLK_APB1_BUS), + JH7100_GDIV(JH7100_CLK_UART0_CORE, "uart0_core", 0, 63, JH7100_CLK_PERH1_SRC), + JH7100_GATE(JH7100_CLK_UART1_APB, "uart1_apb", 0, JH7100_CLK_APB1_BUS), + JH7100_GDIV(JH7100_CLK_UART1_CORE, "uart1_core", 0, 63, JH7100_CLK_PERH1_SRC), + JH7100_GATE(JH7100_CLK_SPI0_APB, "spi0_apb", 0, JH7100_CLK_APB1_BUS), + JH7100_GDIV(JH7100_CLK_SPI0_CORE, "spi0_core", 0, 63, JH7100_CLK_PERH1_SRC), + JH7100_GATE(JH7100_CLK_SPI1_APB, "spi1_apb", 0, JH7100_CLK_APB1_BUS), + JH7100_GDIV(JH7100_CLK_SPI1_CORE, "spi1_core", 0, 63, JH7100_CLK_PERH1_SRC), + JH7100_GATE(JH7100_CLK_I2C0_APB, "i2c0_apb", 0, JH7100_CLK_APB1_BUS), + JH7100_GDIV(JH7100_CLK_I2C0_CORE, "i2c0_core", 0, 63, JH7100_CLK_PERH1_SRC), + JH7100_GATE(JH7100_CLK_I2C1_APB, "i2c1_apb", 0, JH7100_CLK_APB1_BUS), + JH7100_GDIV(JH7100_CLK_I2C1_CORE, "i2c1_core", 0, 63, JH7100_CLK_PERH1_SRC), + JH7100_GATE(JH7100_CLK_GPIO_APB, "gpio_apb", 0, JH7100_CLK_APB1_BUS), + JH7100_GATE(JH7100_CLK_UART2_APB, "uart2_apb", 0, JH7100_CLK_APB2_BUS), + JH7100_GDIV(JH7100_CLK_UART2_CORE, "uart2_core", 0, 63, JH7100_CLK_PERH0_SRC), + JH7100_GATE(JH7100_CLK_UART3_APB, "uart3_apb", 0, JH7100_CLK_APB2_BUS), + JH7100_GDIV(JH7100_CLK_UART3_CORE, "uart3_core", 0, 63, JH7100_CLK_PERH0_SRC), + JH7100_GATE(JH7100_CLK_SPI2_APB, "spi2_apb", 0, JH7100_CLK_APB2_BUS), + JH7100_GDIV(JH7100_CLK_SPI2_CORE, "spi2_core", 0, 63, JH7100_CLK_PERH0_SRC), + JH7100_GATE(JH7100_CLK_SPI3_APB, "spi3_apb", 0, JH7100_CLK_APB2_BUS), + JH7100_GDIV(JH7100_CLK_SPI3_CORE, "spi3_core", 0, 63, JH7100_CLK_PERH0_SRC), + JH7100_GATE(JH7100_CLK_I2C2_APB, "i2c2_apb", 0, JH7100_CLK_APB2_BUS), + JH7100_GDIV(JH7100_CLK_I2C2_CORE, "i2c2_core", 0, 63, JH7100_CLK_PERH0_SRC), + JH7100_GATE(JH7100_CLK_I2C3_APB, "i2c3_apb", 0, JH7100_CLK_APB2_BUS), + JH7100_GDIV(JH7100_CLK_I2C3_CORE, "i2c3_core", 0, 63, JH7100_CLK_PERH0_SRC), + JH7100_GATE(JH7100_CLK_WDTIMER_APB, "wdtimer_apb", 0, JH7100_CLK_APB2_BUS), + JH7100_GDIV(JH7100_CLK_WDT_CORE, "wdt_coreclk", 0, 63, JH7100_CLK_PERH0_SRC), + JH7100_GDIV(JH7100_CLK_TIMER0_CORE, "timer0_coreclk", 0, 63, JH7100_CLK_PERH0_SRC), + JH7100_GDIV(JH7100_CLK_TIMER1_CORE, "timer1_coreclk", 0, 63, JH7100_CLK_PERH0_SRC), + JH7100_GDIV(JH7100_CLK_TIMER2_CORE, "timer2_coreclk", 0, 63, JH7100_CLK_PERH0_SRC), + JH7100_GDIV(JH7100_CLK_TIMER3_CORE, "timer3_coreclk", 0, 63, JH7100_CLK_PERH0_SRC), + JH7100_GDIV(JH7100_CLK_TIMER4_CORE, "timer4_coreclk", 0, 63, JH7100_CLK_PERH0_SRC), + JH7100_GDIV(JH7100_CLK_TIMER5_CORE, "timer5_coreclk", 0, 63, JH7100_CLK_PERH0_SRC), + JH7100_GDIV(JH7100_CLK_TIMER6_CORE, "timer6_coreclk", 0, 63, JH7100_CLK_PERH0_SRC), + JH7100_GATE(JH7100_CLK_VP6INTC_APB, "vp6intc_apb", 0, JH7100_CLK_APB2_BUS), + JH7100_GATE(JH7100_CLK_PWM_APB, "pwm_apb", 0, JH7100_CLK_APB2_BUS), + JH7100_GATE(JH7100_CLK_MSI_APB, "msi_apb", 0, JH7100_CLK_APB2_BUS), + JH7100_GATE(JH7100_CLK_TEMP_APB, "temp_apb", 0, JH7100_CLK_APB2_BUS), + JH7100_GDIV(JH7100_CLK_TEMP_SENSE, "temp_sense", 0, 31, JH7100_CLK_OSC_SYS), + JH7100_GATE(JH7100_CLK_SYSERR_APB, "syserr_apb", 0, JH7100_CLK_APB2_BUS), +}; + +struct jh7100_clk { + struct clk_hw hw; + unsigned int idx; + unsigned int max_div; +}; + +struct jh7100_clk_priv { + /* protect clk enable and set rate/parent from happening at the same time */ + spinlock_t rmw_lock; + struct device *dev; + void __iomem *base; + struct clk_hw *pll[3]; + struct jh7100_clk reg[JH7100_CLK_PLL0_OUT]; +}; + +static struct jh7100_clk *jh7100_clk_from(struct clk_hw *hw) +{ + return container_of(hw, struct jh7100_clk, hw); +} + +static struct jh7100_clk_priv *jh7100_priv_from(struct jh7100_clk *clk) +{ + return container_of(clk, struct jh7100_clk_priv, reg[clk->idx]); +} + +static u32 jh7100_clk_reg_get(struct jh7100_clk *clk) +{ + struct jh7100_clk_priv *priv = jh7100_priv_from(clk); + void __iomem *reg = priv->base + 4 * clk->idx; + + return readl_relaxed(reg); +} + +static void jh7100_clk_reg_rmw(struct jh7100_clk *clk, u32 mask, u32 value) +{ + struct jh7100_clk_priv *priv = jh7100_priv_from(clk); + void __iomem *reg = priv->base + 4 * clk->idx; + unsigned long flags; + + spin_lock_irqsave(&priv->rmw_lock, flags); + value |= readl_relaxed(reg) & ~mask; + writel_relaxed(value, reg); + spin_unlock_irqrestore(&priv->rmw_lock, flags); +} + +static int jh7100_clk_enable(struct clk_hw *hw) +{ + struct jh7100_clk *clk = jh7100_clk_from(hw); + + jh7100_clk_reg_rmw(clk, JH7100_CLK_ENABLE, JH7100_CLK_ENABLE); + return 0; +} + +static void jh7100_clk_disable(struct clk_hw *hw) +{ + struct jh7100_clk *clk = jh7100_clk_from(hw); + + jh7100_clk_reg_rmw(clk, JH7100_CLK_ENABLE, 0); +} + +static int jh7100_clk_is_enabled(struct clk_hw *hw) +{ + struct jh7100_clk *clk = jh7100_clk_from(hw); + + return !!(jh7100_clk_reg_get(clk) & JH7100_CLK_ENABLE); +} + +static unsigned long jh7100_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct jh7100_clk *clk = jh7100_clk_from(hw); + u32 div = jh7100_clk_reg_get(clk) & JH7100_CLK_DIV_MASK; + + return div ? parent_rate / div : 0; +} + +static unsigned long jh7100_clk_bestdiv(struct jh7100_clk *clk, + unsigned long rate, unsigned long parent) +{ + unsigned long max = clk->max_div; + unsigned long div = DIV_ROUND_UP(parent, rate); + + return min(div, max); +} + +static int jh7100_clk_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct jh7100_clk *clk = jh7100_clk_from(hw); + unsigned long parent = req->best_parent_rate; + unsigned long rate = clamp(req->rate, req->min_rate, req->max_rate); + unsigned long div = jh7100_clk_bestdiv(clk, rate, parent); + unsigned long result = parent / div; + + /* + * we want the result clamped by min_rate and max_rate if possible: + * case 1: div hits the max divider value, which means it's less than + * parent / rate, so the result is greater than rate and min_rate in + * particular. we can't do anything about result > max_rate because the + * divider doesn't go any further. + * case 2: div = DIV_ROUND_UP(parent, rate) which means the result is + * always lower or equal to rate and max_rate. however the result may + * turn out lower than min_rate, but then the next higher rate is fine: + * div - 1 = ceil(parent / rate) - 1 < parent / rate + * and thus + * min_rate <= rate < parent / (div - 1) + */ + if (result < req->min_rate && div > 1) + result = parent / (div - 1); + + req->rate = result; + return 0; +} + +static int jh7100_clk_set_rate(struct clk_hw *hw, + unsigned long rate, + unsigned long parent_rate) +{ + struct jh7100_clk *clk = jh7100_clk_from(hw); + unsigned long div = jh7100_clk_bestdiv(clk, rate, parent_rate); + + jh7100_clk_reg_rmw(clk, JH7100_CLK_DIV_MASK, div); + return 0; +} + +static u8 jh7100_clk_get_parent(struct clk_hw *hw) +{ + struct jh7100_clk *clk = jh7100_clk_from(hw); + u32 value = jh7100_clk_reg_get(clk); + + return (value & JH7100_CLK_MUX_MASK) >> JH7100_CLK_MUX_SHIFT; +} + +static int jh7100_clk_set_parent(struct clk_hw *hw, u8 index) +{ + struct jh7100_clk *clk = jh7100_clk_from(hw); + u32 value = (u32)index << JH7100_CLK_MUX_SHIFT; + + jh7100_clk_reg_rmw(clk, JH7100_CLK_MUX_MASK, value); + return 0; +} + +static int jh7100_clk_mux_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + return clk_mux_determine_rate_flags(hw, req, 0); +} + +static int jh7100_clk_get_phase(struct clk_hw *hw) +{ + struct jh7100_clk *clk = jh7100_clk_from(hw); + u32 value = jh7100_clk_reg_get(clk); + + return (value & JH7100_CLK_INVERT) ? 180 : 0; +} + +static int jh7100_clk_set_phase(struct clk_hw *hw, int degrees) +{ + struct jh7100_clk *clk = jh7100_clk_from(hw); + u32 value; + + if (degrees == 0) + value = 0; + else if (degrees == 180) + value = JH7100_CLK_INVERT; + else + return -EINVAL; + + jh7100_clk_reg_rmw(clk, JH7100_CLK_INVERT, value); + return 0; +} + +#ifdef CONFIG_DEBUG_FS +static void jh7100_clk_debug_init(struct clk_hw *hw, struct dentry *dentry) +{ + static const struct debugfs_reg32 jh7100_clk_reg = { + .name = "CTRL", + .offset = 0, + }; + struct jh7100_clk *clk = jh7100_clk_from(hw); + struct jh7100_clk_priv *priv = jh7100_priv_from(clk); + struct debugfs_regset32 *regset; + + regset = devm_kzalloc(priv->dev, sizeof(*regset), GFP_KERNEL); + if (!regset) + return; + + regset->regs = &jh7100_clk_reg; + regset->nregs = 1; + regset->base = priv->base + 4 * clk->idx; + + debugfs_create_regset32("registers", 0400, dentry, regset); +} +#else +#define jh7100_clk_debug_init NULL +#endif + +static const struct clk_ops jh7100_clk_gate_ops = { + .enable = jh7100_clk_enable, + .disable = jh7100_clk_disable, + .is_enabled = jh7100_clk_is_enabled, + .debug_init = jh7100_clk_debug_init, +}; + +static const struct clk_ops jh7100_clk_div_ops = { + .recalc_rate = jh7100_clk_recalc_rate, + .determine_rate = jh7100_clk_determine_rate, + .set_rate = jh7100_clk_set_rate, + .debug_init = jh7100_clk_debug_init, +}; + +static const struct clk_ops jh7100_clk_gdiv_ops = { + .enable = jh7100_clk_enable, + .disable = jh7100_clk_disable, + .is_enabled = jh7100_clk_is_enabled, + .recalc_rate = jh7100_clk_recalc_rate, + .determine_rate = jh7100_clk_determine_rate, + .set_rate = jh7100_clk_set_rate, + .debug_init = jh7100_clk_debug_init, +}; + +static const struct clk_ops jh7100_clk_mux_ops = { + .determine_rate = jh7100_clk_mux_determine_rate, + .set_parent = jh7100_clk_set_parent, + .get_parent = jh7100_clk_get_parent, + .debug_init = jh7100_clk_debug_init, +}; + +static const struct clk_ops jh7100_clk_gmux_ops = { + .enable = jh7100_clk_enable, + .disable = jh7100_clk_disable, + .is_enabled = jh7100_clk_is_enabled, + .determine_rate = jh7100_clk_mux_determine_rate, + .set_parent = jh7100_clk_set_parent, + .get_parent = jh7100_clk_get_parent, + .debug_init = jh7100_clk_debug_init, +}; + +static const struct clk_ops jh7100_clk_inv_ops = { + .get_phase = jh7100_clk_get_phase, + .set_phase = jh7100_clk_set_phase, + .debug_init = jh7100_clk_debug_init, +}; + +static const struct clk_ops *__init jh7100_clk_ops(u32 max) +{ + if (max & JH7100_CLK_DIV_MASK) { + if (max & JH7100_CLK_ENABLE) + return &jh7100_clk_gdiv_ops; + return &jh7100_clk_div_ops; + } + + if (max & JH7100_CLK_MUX_MASK) { + if (max & JH7100_CLK_ENABLE) + return &jh7100_clk_gmux_ops; + return &jh7100_clk_mux_ops; + } + + if (max & JH7100_CLK_ENABLE) + return &jh7100_clk_gate_ops; + + return &jh7100_clk_inv_ops; +} + +static struct clk_hw *jh7100_clk_get(struct of_phandle_args *clkspec, void *data) +{ + struct jh7100_clk_priv *priv = data; + unsigned int idx = clkspec->args[0]; + + if (idx < JH7100_CLK_PLL0_OUT) + return &priv->reg[idx].hw; + + if (idx < JH7100_CLK_END) + return priv->pll[idx - JH7100_CLK_PLL0_OUT]; + + return ERR_PTR(-EINVAL); +} + +static int __init clk_starfive_jh7100_probe(struct platform_device *pdev) +{ + struct jh7100_clk_priv *priv; + unsigned int idx; + int ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + spin_lock_init(&priv->rmw_lock); + priv->dev = &pdev->dev; + priv->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + priv->pll[0] = devm_clk_hw_register_fixed_factor(priv->dev, "pll0_out", + "osc_sys", 0, 40, 1); + if (IS_ERR(priv->pll[0])) + return PTR_ERR(priv->pll[0]); + + priv->pll[1] = devm_clk_hw_register_fixed_factor(priv->dev, "pll1_out", + "osc_sys", 0, 64, 1); + if (IS_ERR(priv->pll[1])) + return PTR_ERR(priv->pll[1]); + + priv->pll[2] = devm_clk_hw_register_fixed_factor(priv->dev, "pll2_out", + "pll2_refclk", 0, 55, 1); + if (IS_ERR(priv->pll[2])) + return PTR_ERR(priv->pll[2]); + + for (idx = 0; idx < JH7100_CLK_PLL0_OUT; idx++) { + u32 max = jh7100_clk_data[idx].max; + struct clk_parent_data parents[4] = {}; + struct clk_init_data init = { + .name = jh7100_clk_data[idx].name, + .ops = jh7100_clk_ops(max), + .parent_data = parents, + .num_parents = ((max & JH7100_CLK_MUX_MASK) >> JH7100_CLK_MUX_SHIFT) + 1, + .flags = jh7100_clk_data[idx].flags, + }; + struct jh7100_clk *clk = &priv->reg[idx]; + unsigned int i; + + for (i = 0; i < init.num_parents; i++) { + unsigned int pidx = jh7100_clk_data[idx].parents[i]; + + if (pidx < JH7100_CLK_PLL0_OUT) + parents[i].hw = &priv->reg[pidx].hw; + else if (pidx < JH7100_CLK_END) + parents[i].hw = priv->pll[pidx - JH7100_CLK_PLL0_OUT]; + else if (pidx == JH7100_CLK_OSC_SYS) + parents[i].fw_name = "osc_sys"; + else if (pidx == JH7100_CLK_OSC_AUD) + parents[i].fw_name = "osc_aud"; + else if (pidx == JH7100_CLK_GMAC_RMII_REF) + parents[i].fw_name = "gmac_rmii_ref"; + else if (pidx == JH7100_CLK_GMAC_GR_MII_RX) + parents[i].fw_name = "gmac_gr_mii_rxclk"; + } + + clk->hw.init = &init; + clk->idx = idx; + clk->max_div = max & JH7100_CLK_DIV_MASK; + + ret = devm_clk_hw_register(priv->dev, &clk->hw); + if (ret) + return ret; + } + + return devm_of_clk_add_hw_provider(priv->dev, jh7100_clk_get, priv); +} + +static const struct of_device_id clk_starfive_jh7100_match[] = { + { .compatible = "starfive,jh7100-clkgen" }, + { /* sentinel */ } +}; + +static struct platform_driver clk_starfive_jh7100_driver = { + .driver = { + .name = "clk-starfive-jh7100", + .of_match_table = clk_starfive_jh7100_match, + .suppress_bind_attrs = true, + }, +}; +builtin_platform_driver_probe(clk_starfive_jh7100_driver, clk_starfive_jh7100_probe); From 5bd2429ec3788bdd76cef84c9ba1af35b65519cd Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 25 Jun 2021 11:30:00 +0200 Subject: [PATCH 07/80] dt-bindings: reset: Add StarFive JH7100 reset definitions commit 810e287e83b69ff8563bde15cae9120c802ac5d7 upstream. Add all resets for the StarFive JH7100 reset controller. Based on work by Ahmad Fatoum for Barebox, with "JH7100_" prefixes added to all definitions. Acked-by: Rob Herring Signed-off-by: Geert Uytterhoeven Signed-off-by: Emil Renner Berthing --- include/dt-bindings/reset/starfive-jh7100.h | 126 ++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 include/dt-bindings/reset/starfive-jh7100.h diff --git a/include/dt-bindings/reset/starfive-jh7100.h b/include/dt-bindings/reset/starfive-jh7100.h new file mode 100644 index 0000000000000..540e19254f390 --- /dev/null +++ b/include/dt-bindings/reset/starfive-jh7100.h @@ -0,0 +1,126 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/* + * Copyright (C) 2021 Ahmad Fatoum, Pengutronix + */ + +#ifndef __DT_BINDINGS_RESET_STARFIVE_JH7100_H__ +#define __DT_BINDINGS_RESET_STARFIVE_JH7100_H__ + +#define JH7100_RSTN_DOM3AHB_BUS 0 +#define JH7100_RSTN_DOM7AHB_BUS 1 +#define JH7100_RST_U74 2 +#define JH7100_RSTN_U74_AXI 3 +#define JH7100_RSTN_SGDMA2P_AHB 4 +#define JH7100_RSTN_SGDMA2P_AXI 5 +#define JH7100_RSTN_DMA2PNOC_AXI 6 +#define JH7100_RSTN_DLA_AXI 7 +#define JH7100_RSTN_DLANOC_AXI 8 +#define JH7100_RSTN_DLA_APB 9 +#define JH7100_RST_VP6_DRESET 10 +#define JH7100_RST_VP6_BRESET 11 +#define JH7100_RSTN_VP6_AXI 12 +#define JH7100_RSTN_VDECBRG_MAIN 13 +#define JH7100_RSTN_VDEC_AXI 14 +#define JH7100_RSTN_VDEC_BCLK 15 +#define JH7100_RSTN_VDEC_CCLK 16 +#define JH7100_RSTN_VDEC_APB 17 +#define JH7100_RSTN_JPEG_AXI 18 +#define JH7100_RSTN_JPEG_CCLK 19 +#define JH7100_RSTN_JPEG_APB 20 +#define JH7100_RSTN_JPCGC300_MAIN 21 +#define JH7100_RSTN_GC300_2X 22 +#define JH7100_RSTN_GC300_AXI 23 +#define JH7100_RSTN_GC300_AHB 24 +#define JH7100_RSTN_VENC_AXI 25 +#define JH7100_RSTN_VENCBRG_MAIN 26 +#define JH7100_RSTN_VENC_BCLK 27 +#define JH7100_RSTN_VENC_CCLK 28 +#define JH7100_RSTN_VENC_APB 29 +#define JH7100_RSTN_DDRPHY_APB 30 +#define JH7100_RSTN_NOC_ROB 31 +#define JH7100_RSTN_NOC_COG 32 +#define JH7100_RSTN_HIFI4_AXI 33 +#define JH7100_RSTN_HIFI4NOC_AXI 34 +#define JH7100_RST_HIFI4_DRESET 35 +#define JH7100_RST_HIFI4_BRESET 36 +#define JH7100_RSTN_USB_AXI 37 +#define JH7100_RSTN_USBNOC_AXI 38 +#define JH7100_RSTN_SGDMA1P_AXI 39 +#define JH7100_RSTN_DMA1P_AXI 40 +#define JH7100_RSTN_X2C_AXI 41 +#define JH7100_RSTN_NNE_AHB 42 +#define JH7100_RSTN_NNE_AXI 43 +#define JH7100_RSTN_NNENOC_AXI 44 +#define JH7100_RSTN_DLASLV_AXI 45 +#define JH7100_RSTN_DSPX2C_AXI 46 +#define JH7100_RSTN_VIN_SRC 47 +#define JH7100_RSTN_ISPSLV_AXI 48 +#define JH7100_RSTN_VIN_AXI 49 +#define JH7100_RSTN_VINNOC_AXI 50 +#define JH7100_RSTN_ISP0_AXI 51 +#define JH7100_RSTN_ISP0NOC_AXI 52 +#define JH7100_RSTN_ISP1_AXI 53 +#define JH7100_RSTN_ISP1NOC_AXI 54 +#define JH7100_RSTN_VOUT_SRC 55 +#define JH7100_RSTN_DISP_AXI 56 +#define JH7100_RSTN_DISPNOC_AXI 57 +#define JH7100_RSTN_SDIO0_AHB 58 +#define JH7100_RSTN_SDIO1_AHB 59 +#define JH7100_RSTN_GMAC_AHB 60 +#define JH7100_RSTN_SPI2AHB_AHB 61 +#define JH7100_RSTN_SPI2AHB_CORE 62 +#define JH7100_RSTN_EZMASTER_AHB 63 +#define JH7100_RST_E24 64 +#define JH7100_RSTN_QSPI_AHB 65 +#define JH7100_RSTN_QSPI_CORE 66 +#define JH7100_RSTN_QSPI_APB 67 +#define JH7100_RSTN_SEC_AHB 68 +#define JH7100_RSTN_AES 69 +#define JH7100_RSTN_PKA 70 +#define JH7100_RSTN_SHA 71 +#define JH7100_RSTN_TRNG_APB 72 +#define JH7100_RSTN_OTP_APB 73 +#define JH7100_RSTN_UART0_APB 74 +#define JH7100_RSTN_UART0_CORE 75 +#define JH7100_RSTN_UART1_APB 76 +#define JH7100_RSTN_UART1_CORE 77 +#define JH7100_RSTN_SPI0_APB 78 +#define JH7100_RSTN_SPI0_CORE 79 +#define JH7100_RSTN_SPI1_APB 80 +#define JH7100_RSTN_SPI1_CORE 81 +#define JH7100_RSTN_I2C0_APB 82 +#define JH7100_RSTN_I2C0_CORE 83 +#define JH7100_RSTN_I2C1_APB 84 +#define JH7100_RSTN_I2C1_CORE 85 +#define JH7100_RSTN_GPIO_APB 86 +#define JH7100_RSTN_UART2_APB 87 +#define JH7100_RSTN_UART2_CORE 88 +#define JH7100_RSTN_UART3_APB 89 +#define JH7100_RSTN_UART3_CORE 90 +#define JH7100_RSTN_SPI2_APB 91 +#define JH7100_RSTN_SPI2_CORE 92 +#define JH7100_RSTN_SPI3_APB 93 +#define JH7100_RSTN_SPI3_CORE 94 +#define JH7100_RSTN_I2C2_APB 95 +#define JH7100_RSTN_I2C2_CORE 96 +#define JH7100_RSTN_I2C3_APB 97 +#define JH7100_RSTN_I2C3_CORE 98 +#define JH7100_RSTN_WDTIMER_APB 99 +#define JH7100_RSTN_WDT 100 +#define JH7100_RSTN_TIMER0 101 +#define JH7100_RSTN_TIMER1 102 +#define JH7100_RSTN_TIMER2 103 +#define JH7100_RSTN_TIMER3 104 +#define JH7100_RSTN_TIMER4 105 +#define JH7100_RSTN_TIMER5 106 +#define JH7100_RSTN_TIMER6 107 +#define JH7100_RSTN_VP6INTC_APB 108 +#define JH7100_RSTN_PWM_APB 109 +#define JH7100_RSTN_MSI_APB 110 +#define JH7100_RSTN_TEMP_APB 111 +#define JH7100_RSTN_TEMP_SENSE 112 +#define JH7100_RSTN_SYSERR_APB 113 + +#define JH7100_RSTN_END 114 + +#endif /* __DT_BINDINGS_RESET_STARFIVE_JH7100_H__ */ From 30fbb880ff361082d82ff5f8b904fcd560c6a0c9 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Sun, 19 Sep 2021 14:34:34 +0200 Subject: [PATCH 08/80] dt-bindings: reset: Add Starfive JH7100 reset bindings commit d7d456a5201d2e707318bbdc4fb69a3407eed29e upstream. Add bindings for the reset controller on the JH7100 RISC-V SoC by StarFive Ltd. This is a test chip for their upcoming JH7110 SoC. Reviewed-by: Geert Uytterhoeven Reviewed-by: Rob Herring Signed-off-by: Emil Renner Berthing --- .../bindings/reset/starfive,jh7100-reset.yaml | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 Documentation/devicetree/bindings/reset/starfive,jh7100-reset.yaml diff --git a/Documentation/devicetree/bindings/reset/starfive,jh7100-reset.yaml b/Documentation/devicetree/bindings/reset/starfive,jh7100-reset.yaml new file mode 100644 index 0000000000000..300359a5e14b4 --- /dev/null +++ b/Documentation/devicetree/bindings/reset/starfive,jh7100-reset.yaml @@ -0,0 +1,38 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/reset/starfive,jh7100-reset.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive JH7100 SoC Reset Controller Device Tree Bindings + +maintainers: + - Emil Renner Berthing + +properties: + compatible: + enum: + - starfive,jh7100-reset + + reg: + maxItems: 1 + + "#reset-cells": + const: 1 + +required: + - compatible + - reg + - "#reset-cells" + +additionalProperties: false + +examples: + - | + reset-controller@11840000 { + compatible = "starfive,jh7100-reset"; + reg = <0x11840000 0x10000>; + #reset-cells = <1>; + }; + +... From 8acdff1be940033239f9b55fe4164706a010bf7a Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Sun, 19 Sep 2021 14:21:05 +0200 Subject: [PATCH 09/80] reset: starfive-jh7100: Add StarFive JH7100 reset driver commit 0be3a1595bf8c7f39153be02c9aae61dd2108576 upstream. Add a driver for the StarFive JH7100 reset controller. Reviewed-by: Andy Shevchenko Signed-off-by: Emil Renner Berthing --- MAINTAINERS | 7 ++ drivers/reset/Kconfig | 7 ++ drivers/reset/Makefile | 1 + drivers/reset/reset-starfive-jh7100.c | 172 ++++++++++++++++++++++++++ 4 files changed, 187 insertions(+) create mode 100644 drivers/reset/reset-starfive-jh7100.c diff --git a/MAINTAINERS b/MAINTAINERS index d974f4fb7b3a4..1690a1eca564b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -18163,6 +18163,13 @@ F: Documentation/devicetree/bindings/clock/starfive,jh7100-clkgen.yaml F: drivers/clk/starfive/clk-starfive-jh7100.c F: include/dt-bindings/clock/starfive-jh7100.h +STARFIVE JH7100 RESET CONTROLLER DRIVER +M: Emil Renner Berthing +S: Maintained +F: Documentation/devicetree/bindings/reset/starfive,jh7100-reset.yaml +F: drivers/reset/reset-starfive-jh7100.c +F: include/dt-bindings/reset/starfive-jh7100.h + STATIC BRANCH/CALL M: Peter Zijlstra M: Josh Poimboeuf diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index 85024eb1d2eac..6f8ba0ddc05fe 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -224,6 +224,13 @@ config RESET_SOCFPGA This enables the reset driver for the SoCFPGA ARMv7 platforms. This driver gets initialized early during platform init calls. +config RESET_STARFIVE_JH7100 + bool "StarFive JH7100 Reset Driver" + depends on SOC_STARFIVE || COMPILE_TEST + default SOC_STARFIVE + help + This enables the reset controller driver for the StarFive JH7100 SoC. + config RESET_SUNXI bool "Allwinner SoCs Reset Driver" if COMPILE_TEST && !ARCH_SUNXI default ARCH_SUNXI diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index 21d46d8869ff4..bd0a97be18b50 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_RESET_RZG2L_USBPHY_CTRL) += reset-rzg2l-usbphy-ctrl.o obj-$(CONFIG_RESET_SCMI) += reset-scmi.o obj-$(CONFIG_RESET_SIMPLE) += reset-simple.o obj-$(CONFIG_RESET_SOCFPGA) += reset-socfpga.o +obj-$(CONFIG_RESET_STARFIVE_JH7100) += reset-starfive-jh7100.o obj-$(CONFIG_RESET_SUNXI) += reset-sunxi.o obj-$(CONFIG_RESET_TI_SCI) += reset-ti-sci.o obj-$(CONFIG_RESET_TI_SYSCON) += reset-ti-syscon.o diff --git a/drivers/reset/reset-starfive-jh7100.c b/drivers/reset/reset-starfive-jh7100.c new file mode 100644 index 0000000000000..e28a19d271cbc --- /dev/null +++ b/drivers/reset/reset-starfive-jh7100.c @@ -0,0 +1,172 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Reset driver for the StarFive JH7100 SoC + * + * Copyright (C) 2021 Emil Renner Berthing + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +/* register offsets */ +#define JH7100_RESET_ASSERT0 0x00 +#define JH7100_RESET_ASSERT1 0x04 +#define JH7100_RESET_ASSERT2 0x08 +#define JH7100_RESET_ASSERT3 0x0c +#define JH7100_RESET_STATUS0 0x10 +#define JH7100_RESET_STATUS1 0x14 +#define JH7100_RESET_STATUS2 0x18 +#define JH7100_RESET_STATUS3 0x1c + +/* + * Writing a 1 to the n'th bit of the m'th ASSERT register asserts + * line 32m + n, and writing a 0 deasserts the same line. + * Most reset lines have their status inverted so a 0 bit in the STATUS + * register means the line is asserted and a 1 means it's deasserted. A few + * lines don't though, so store the expected value of the status registers when + * all lines are asserted. + */ +static const u64 jh7100_reset_asserted[2] = { + /* STATUS0 */ + BIT_ULL_MASK(JH7100_RST_U74) | + BIT_ULL_MASK(JH7100_RST_VP6_DRESET) | + BIT_ULL_MASK(JH7100_RST_VP6_BRESET) | + /* STATUS1 */ + BIT_ULL_MASK(JH7100_RST_HIFI4_DRESET) | + BIT_ULL_MASK(JH7100_RST_HIFI4_BRESET), + /* STATUS2 */ + BIT_ULL_MASK(JH7100_RST_E24) | + /* STATUS3 */ + 0, +}; + +struct jh7100_reset { + struct reset_controller_dev rcdev; + /* protect registers against concurrent read-modify-write */ + spinlock_t lock; + void __iomem *base; +}; + +static inline struct jh7100_reset * +jh7100_reset_from(struct reset_controller_dev *rcdev) +{ + return container_of(rcdev, struct jh7100_reset, rcdev); +} + +static int jh7100_reset_update(struct reset_controller_dev *rcdev, + unsigned long id, bool assert) +{ + struct jh7100_reset *data = jh7100_reset_from(rcdev); + unsigned long offset = BIT_ULL_WORD(id); + u64 mask = BIT_ULL_MASK(id); + void __iomem *reg_assert = data->base + JH7100_RESET_ASSERT0 + offset * sizeof(u64); + void __iomem *reg_status = data->base + JH7100_RESET_STATUS0 + offset * sizeof(u64); + u64 done = jh7100_reset_asserted[offset] & mask; + u64 value; + unsigned long flags; + int ret; + + if (!assert) + done ^= mask; + + spin_lock_irqsave(&data->lock, flags); + + value = readq(reg_assert); + if (assert) + value |= mask; + else + value &= ~mask; + writeq(value, reg_assert); + + /* if the associated clock is gated, deasserting might otherwise hang forever */ + ret = readq_poll_timeout_atomic(reg_status, value, (value & mask) == done, 0, 1000); + + spin_unlock_irqrestore(&data->lock, flags); + return ret; +} + +static int jh7100_reset_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + return jh7100_reset_update(rcdev, id, true); +} + +static int jh7100_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + return jh7100_reset_update(rcdev, id, false); +} + +static int jh7100_reset_reset(struct reset_controller_dev *rcdev, + unsigned long id) +{ + int ret; + + ret = jh7100_reset_assert(rcdev, id); + if (ret) + return ret; + + return jh7100_reset_deassert(rcdev, id); +} + +static int jh7100_reset_status(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct jh7100_reset *data = jh7100_reset_from(rcdev); + unsigned long offset = BIT_ULL_WORD(id); + u64 mask = BIT_ULL_MASK(id); + void __iomem *reg_status = data->base + JH7100_RESET_STATUS0 + offset * sizeof(u64); + u64 value = readq(reg_status); + + return !((value ^ jh7100_reset_asserted[offset]) & mask); +} + +static const struct reset_control_ops jh7100_reset_ops = { + .assert = jh7100_reset_assert, + .deassert = jh7100_reset_deassert, + .reset = jh7100_reset_reset, + .status = jh7100_reset_status, +}; + +static int __init jh7100_reset_probe(struct platform_device *pdev) +{ + struct jh7100_reset *data; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(data->base)) + return PTR_ERR(data->base); + + data->rcdev.ops = &jh7100_reset_ops; + data->rcdev.owner = THIS_MODULE; + data->rcdev.nr_resets = JH7100_RSTN_END; + data->rcdev.dev = &pdev->dev; + data->rcdev.of_node = pdev->dev.of_node; + spin_lock_init(&data->lock); + + return devm_reset_controller_register(&pdev->dev, &data->rcdev); +} + +static const struct of_device_id jh7100_reset_dt_ids[] = { + { .compatible = "starfive,jh7100-reset" }, + { /* sentinel */ } +}; + +static struct platform_driver jh7100_reset_driver = { + .driver = { + .name = "jh7100-reset", + .of_match_table = jh7100_reset_dt_ids, + .suppress_bind_attrs = true, + }, +}; +builtin_platform_driver_probe(jh7100_reset_driver, jh7100_reset_probe); From 1c0610c35f7cbfdf63dac35c76af66b965f48574 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Tue, 6 Jul 2021 20:19:06 +0200 Subject: [PATCH 10/80] dt-bindings: pinctrl: Add StarFive pinctrl definitions commit 3021114b3d172cf80c074c81425741f9e26c6679 upstream. Add definitons for pins and GPIO input, output and output enable signals on the StarFive JH7100 SoC. Acked-by: Rob Herring Signed-off-by: Emil Renner Berthing --- .../dt-bindings/pinctrl/pinctrl-starfive.h | 275 ++++++++++++++++++ 1 file changed, 275 insertions(+) create mode 100644 include/dt-bindings/pinctrl/pinctrl-starfive.h diff --git a/include/dt-bindings/pinctrl/pinctrl-starfive.h b/include/dt-bindings/pinctrl/pinctrl-starfive.h new file mode 100644 index 0000000000000..de4f75c2c9e85 --- /dev/null +++ b/include/dt-bindings/pinctrl/pinctrl-starfive.h @@ -0,0 +1,275 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/* + * Copyright (C) 2021 Emil Renner Berthing + */ + +#ifndef __DT_BINDINGS_PINCTRL_STARFIVE_H__ +#define __DT_BINDINGS_PINCTRL_STARFIVE_H__ + +#define PAD_GPIO_OFFSET 0 +#define PAD_FUNC_SHARE_OFFSET 64 +#define PAD_GPIO(x) (PAD_GPIO_OFFSET + (x)) +#define PAD_FUNC_SHARE(x) (PAD_FUNC_SHARE_OFFSET + (x)) + +/* + * GPIOMUX bits: + * | 31 - 24 | 23 - 16 | 15 - 8 | 7 | 6 | 5 - 0 | + * | dout | doen | din | dout rev | doen rev | gpio nr | + * + * dout: output signal + * doen: output enable signal + * din: optional input signal, 0xff = none + * dout rev: output signal reverse bit + * doen rev: output enable signal reverse bit + * gpio nr: gpio number, 0 - 63 + */ +#define GPIOMUX(n, dout, doen, din) ( \ + (((dout) & 0x80000000) >> (31 - 7)) | (((dout) & 0xff) << 24) | \ + (((doen) & 0x80000000) >> (31 - 6)) | (((doen) & 0xff) << 16) | \ + (((din) & 0xff) << 8) | \ + ((n) & 0x3f)) + +#define GPO_REVERSE 0x80000000 + +#define GPO_LOW 0 +#define GPO_HIGH 1 +#define GPO_ENABLE 0 +#define GPO_DISABLE 1 +#define GPO_CLK_GMAC_PAPHYREF 2 +#define GPO_JTAG_TDO 3 +#define GPO_JTAG_TDO_OEN 4 +#define GPO_DMIC_CLK_OUT 5 +#define GPO_DSP_JTDOEN_PAD 6 +#define GPO_DSP_JTDO_PAD 7 +#define GPO_I2C0_PAD_SCK_OE 8 +#define GPO_I2C0_PAD_SCK_OEN (GPO_I2C0_PAD_SCK_OE | GPO_REVERSE) +#define GPO_I2C0_PAD_SDA_OE 9 +#define GPO_I2C0_PAD_SDA_OEN (GPO_I2C0_PAD_SDA_OE | GPO_REVERSE) +#define GPO_I2C1_PAD_SCK_OE 10 +#define GPO_I2C1_PAD_SCK_OEN (GPO_I2C1_PAD_SCK_OE | GPO_REVERSE) +#define GPO_I2C1_PAD_SDA_OE 11 +#define GPO_I2C1_PAD_SDA_OEN (GPO_I2C1_PAD_SDA_OE | GPO_REVERSE) +#define GPO_I2C2_PAD_SCK_OE 12 +#define GPO_I2C2_PAD_SCK_OEN (GPO_I2C2_PAD_SCK_OE | GPO_REVERSE) +#define GPO_I2C2_PAD_SDA_OE 13 +#define GPO_I2C2_PAD_SDA_OEN (GPO_I2C2_PAD_SDA_OE | GPO_REVERSE) +#define GPO_I2C3_PAD_SCK_OE 14 +#define GPO_I2C3_PAD_SCK_OEN (GPO_I2C3_PAD_SCK_OE | GPO_REVERSE) +#define GPO_I2C3_PAD_SDA_OE 15 +#define GPO_I2C3_PAD_SDA_OEN (GPO_I2C3_PAD_SDA_OE | GPO_REVERSE) +#define GPO_I2SRX_BCLK_OUT 16 +#define GPO_I2SRX_BCLK_OUT_OEN 17 +#define GPO_I2SRX_LRCK_OUT 18 +#define GPO_I2SRX_LRCK_OUT_OEN 19 +#define GPO_I2SRX_MCLK_OUT 20 +#define GPO_I2STX_BCLK_OUT 21 +#define GPO_I2STX_BCLK_OUT_OEN 22 +#define GPO_I2STX_LRCK_OUT 23 +#define GPO_I2STX_LRCK_OUT_OEN 24 +#define GPO_I2STX_MCLK_OUT 25 +#define GPO_I2STX_SDOUT0 26 +#define GPO_I2STX_SDOUT1 27 +#define GPO_LCD_PAD_CSM_N 28 +#define GPO_PWM_PAD_OE_N_BIT0 29 +#define GPO_PWM_PAD_OE_N_BIT1 30 +#define GPO_PWM_PAD_OE_N_BIT2 31 +#define GPO_PWM_PAD_OE_N_BIT3 32 +#define GPO_PWM_PAD_OE_N_BIT4 33 +#define GPO_PWM_PAD_OE_N_BIT5 34 +#define GPO_PWM_PAD_OE_N_BIT6 35 +#define GPO_PWM_PAD_OE_N_BIT7 36 +#define GPO_PWM_PAD_OUT_BIT0 37 +#define GPO_PWM_PAD_OUT_BIT1 38 +#define GPO_PWM_PAD_OUT_BIT2 39 +#define GPO_PWM_PAD_OUT_BIT3 40 +#define GPO_PWM_PAD_OUT_BIT4 41 +#define GPO_PWM_PAD_OUT_BIT5 42 +#define GPO_PWM_PAD_OUT_BIT6 43 +#define GPO_PWM_PAD_OUT_BIT7 44 +#define GPO_PWMDAC_LEFT_OUT 45 +#define GPO_PWMDAC_RIGHT_OUT 46 +#define GPO_QSPI_CSN1_OUT 47 +#define GPO_QSPI_CSN2_OUT 48 +#define GPO_QSPI_CSN3_OUT 49 +#define GPO_REGISTER23_SCFG_CMSENSOR_RST0 50 +#define GPO_REGISTER23_SCFG_CMSENSOR_RST1 51 +#define GPO_REGISTER32_SCFG_GMAC_PHY_RSTN 52 +#define GPO_SDIO0_PAD_CARD_POWER_EN 53 +#define GPO_SDIO0_PAD_CCLK_OUT 54 +#define GPO_SDIO0_PAD_CCMD_OE 55 +#define GPO_SDIO0_PAD_CCMD_OEN (GPO_SDIO0_PAD_CCMD_OE | GPO_REVERSE) +#define GPO_SDIO0_PAD_CCMD_OUT 56 +#define GPO_SDIO0_PAD_CDATA_OE_BIT0 57 +#define GPO_SDIO0_PAD_CDATA_OEN_BIT0 (GPO_SDIO0_PAD_CDATA_OE_BIT0 | GPO_REVERSE) +#define GPO_SDIO0_PAD_CDATA_OE_BIT1 58 +#define GPO_SDIO0_PAD_CDATA_OEN_BIT1 (GPO_SDIO0_PAD_CDATA_OE_BIT1 | GPO_REVERSE) +#define GPO_SDIO0_PAD_CDATA_OE_BIT2 59 +#define GPO_SDIO0_PAD_CDATA_OEN_BIT2 (GPO_SDIO0_PAD_CDATA_OE_BIT2 | GPO_REVERSE) +#define GPO_SDIO0_PAD_CDATA_OE_BIT3 60 +#define GPO_SDIO0_PAD_CDATA_OEN_BIT3 (GPO_SDIO0_PAD_CDATA_OE_BIT3 | GPO_REVERSE) +#define GPO_SDIO0_PAD_CDATA_OE_BIT4 61 +#define GPO_SDIO0_PAD_CDATA_OEN_BIT4 (GPO_SDIO0_PAD_CDATA_OE_BIT4 | GPO_REVERSE) +#define GPO_SDIO0_PAD_CDATA_OE_BIT5 62 +#define GPO_SDIO0_PAD_CDATA_OEN_BIT5 (GPO_SDIO0_PAD_CDATA_OE_BIT5 | GPO_REVERSE) +#define GPO_SDIO0_PAD_CDATA_OE_BIT6 63 +#define GPO_SDIO0_PAD_CDATA_OEN_BIT6 (GPO_SDIO0_PAD_CDATA_OE_BIT6 | GPO_REVERSE) +#define GPO_SDIO0_PAD_CDATA_OE_BIT7 64 +#define GPO_SDIO0_PAD_CDATA_OEN_BIT7 (GPO_SDIO0_PAD_CDATA_OE_BIT7 | GPO_REVERSE) +#define GPO_SDIO0_PAD_CDATA_OUT_BIT0 65 +#define GPO_SDIO0_PAD_CDATA_OUT_BIT1 66 +#define GPO_SDIO0_PAD_CDATA_OUT_BIT2 67 +#define GPO_SDIO0_PAD_CDATA_OUT_BIT3 68 +#define GPO_SDIO0_PAD_CDATA_OUT_BIT4 69 +#define GPO_SDIO0_PAD_CDATA_OUT_BIT5 70 +#define GPO_SDIO0_PAD_CDATA_OUT_BIT6 71 +#define GPO_SDIO0_PAD_CDATA_OUT_BIT7 72 +#define GPO_SDIO0_PAD_RST_N 73 +#define GPO_SDIO1_PAD_CARD_POWER_EN 74 +#define GPO_SDIO1_PAD_CCLK_OUT 75 +#define GPO_SDIO1_PAD_CCMD_OE 76 +#define GPO_SDIO1_PAD_CCMD_OEN (GPO_SDIO1_PAD_CCMD_OE | GPO_REVERSE) +#define GPO_SDIO1_PAD_CCMD_OUT 77 +#define GPO_SDIO1_PAD_CDATA_OE_BIT0 78 +#define GPO_SDIO1_PAD_CDATA_OEN_BIT0 (GPO_SDIO1_PAD_CDATA_OE_BIT0 | GPO_REVERSE) +#define GPO_SDIO1_PAD_CDATA_OE_BIT1 79 +#define GPO_SDIO1_PAD_CDATA_OEN_BIT1 (GPO_SDIO1_PAD_CDATA_OE_BIT1 | GPO_REVERSE) +#define GPO_SDIO1_PAD_CDATA_OE_BIT2 80 +#define GPO_SDIO1_PAD_CDATA_OEN_BIT2 (GPO_SDIO1_PAD_CDATA_OE_BIT2 | GPO_REVERSE) +#define GPO_SDIO1_PAD_CDATA_OE_BIT3 81 +#define GPO_SDIO1_PAD_CDATA_OEN_BIT3 (GPO_SDIO1_PAD_CDATA_OE_BIT3 | GPO_REVERSE) +#define GPO_SDIO1_PAD_CDATA_OE_BIT4 82 +#define GPO_SDIO1_PAD_CDATA_OEN_BIT4 (GPO_SDIO1_PAD_CDATA_OE_BIT4 | GPO_REVERSE) +#define GPO_SDIO1_PAD_CDATA_OE_BIT5 83 +#define GPO_SDIO1_PAD_CDATA_OEN_BIT5 (GPO_SDIO1_PAD_CDATA_OE_BIT5 | GPO_REVERSE) +#define GPO_SDIO1_PAD_CDATA_OE_BIT6 84 +#define GPO_SDIO1_PAD_CDATA_OEN_BIT6 (GPO_SDIO1_PAD_CDATA_OE_BIT6 | GPO_REVERSE) +#define GPO_SDIO1_PAD_CDATA_OE_BIT7 85 +#define GPO_SDIO1_PAD_CDATA_OEN_BIT7 (GPO_SDIO1_PAD_CDATA_OE_BIT7 | GPO_REVERSE) +#define GPO_SDIO1_PAD_CDATA_OUT_BIT0 86 +#define GPO_SDIO1_PAD_CDATA_OUT_BIT1 87 +#define GPO_SDIO1_PAD_CDATA_OUT_BIT2 88 +#define GPO_SDIO1_PAD_CDATA_OUT_BIT3 89 +#define GPO_SDIO1_PAD_CDATA_OUT_BIT4 90 +#define GPO_SDIO1_PAD_CDATA_OUT_BIT5 91 +#define GPO_SDIO1_PAD_CDATA_OUT_BIT6 92 +#define GPO_SDIO1_PAD_CDATA_OUT_BIT7 93 +#define GPO_SDIO1_PAD_RST_N 94 +#define GPO_SPDIF_TX_SDOUT 95 +#define GPO_SPDIF_TX_SDOUT_OEN 96 +#define GPO_SPI0_PAD_OE_N 97 +#define GPO_SPI0_PAD_SCK_OUT 98 +#define GPO_SPI0_PAD_SS_0_N 99 +#define GPO_SPI0_PAD_SS_1_N 100 +#define GPO_SPI0_PAD_TXD 101 +#define GPO_SPI1_PAD_OE_N 102 +#define GPO_SPI1_PAD_SCK_OUT 103 +#define GPO_SPI1_PAD_SS_0_N 104 +#define GPO_SPI1_PAD_SS_1_N 105 +#define GPO_SPI1_PAD_TXD 106 +#define GPO_SPI2_PAD_OE_N 107 +#define GPO_SPI2_PAD_SCK_OUT 108 +#define GPO_SPI2_PAD_SS_0_N 109 +#define GPO_SPI2_PAD_SS_1_N 110 +#define GPO_SPI2_PAD_TXD 111 +#define GPO_SPI2AHB_PAD_OE_N_BIT0 112 +#define GPO_SPI2AHB_PAD_OE_N_BIT1 113 +#define GPO_SPI2AHB_PAD_OE_N_BIT2 114 +#define GPO_SPI2AHB_PAD_OE_N_BIT3 115 +#define GPO_SPI2AHB_PAD_TXD_BIT0 116 +#define GPO_SPI2AHB_PAD_TXD_BIT1 117 +#define GPO_SPI2AHB_PAD_TXD_BIT2 118 +#define GPO_SPI2AHB_PAD_TXD_BIT3 119 +#define GPO_SPI3_PAD_OE_N 120 +#define GPO_SPI3_PAD_SCK_OUT 121 +#define GPO_SPI3_PAD_SS_0_N 122 +#define GPO_SPI3_PAD_SS_1_N 123 +#define GPO_SPI3_PAD_TXD 124 +#define GPO_UART0_PAD_DTRN 125 +#define GPO_UART0_PAD_RTSN 126 +#define GPO_UART0_PAD_SOUT 127 +#define GPO_UART1_PAD_SOUT 128 +#define GPO_UART2_PAD_DTR_N 129 +#define GPO_UART2_PAD_RTS_N 130 +#define GPO_UART2_PAD_SOUT 131 +#define GPO_UART3_PAD_SOUT 132 +#define GPO_USB_DRV_BUS 133 + +#define GPI_CPU_JTAG_TCK 0 +#define GPI_CPU_JTAG_TDI 1 +#define GPI_CPU_JTAG_TMS 2 +#define GPI_CPU_JTAG_TRST 3 +#define GPI_DMIC_SDIN_BIT0 4 +#define GPI_DMIC_SDIN_BIT1 5 +#define GPI_DSP_JTCK_PAD 6 +#define GPI_DSP_JTDI_PAD 7 +#define GPI_DSP_JTMS_PAD 8 +#define GPI_DSP_TRST_PAD 9 +#define GPI_I2C0_PAD_SCK_IN 10 +#define GPI_I2C0_PAD_SDA_IN 11 +#define GPI_I2C1_PAD_SCK_IN 12 +#define GPI_I2C1_PAD_SDA_IN 13 +#define GPI_I2C2_PAD_SCK_IN 14 +#define GPI_I2C2_PAD_SDA_IN 15 +#define GPI_I2C3_PAD_SCK_IN 16 +#define GPI_I2C3_PAD_SDA_IN 17 +#define GPI_I2SRX_BCLK_IN 18 +#define GPI_I2SRX_LRCK_IN 19 +#define GPI_I2SRX_SDIN_BIT0 20 +#define GPI_I2SRX_SDIN_BIT1 21 +#define GPI_I2SRX_SDIN_BIT2 22 +#define GPI_I2STX_BCLK_IN 23 +#define GPI_I2STX_LRCK_IN 24 +#define GPI_SDIO0_PAD_CARD_DETECT_N 25 +#define GPI_SDIO0_PAD_CARD_WRITE_PRT 26 +#define GPI_SDIO0_PAD_CCMD_IN 27 +#define GPI_SDIO0_PAD_CDATA_IN_BIT0 28 +#define GPI_SDIO0_PAD_CDATA_IN_BIT1 29 +#define GPI_SDIO0_PAD_CDATA_IN_BIT2 30 +#define GPI_SDIO0_PAD_CDATA_IN_BIT3 31 +#define GPI_SDIO0_PAD_CDATA_IN_BIT4 32 +#define GPI_SDIO0_PAD_CDATA_IN_BIT5 33 +#define GPI_SDIO0_PAD_CDATA_IN_BIT6 34 +#define GPI_SDIO0_PAD_CDATA_IN_BIT7 35 +#define GPI_SDIO1_PAD_CARD_DETECT_N 36 +#define GPI_SDIO1_PAD_CARD_WRITE_PRT 37 +#define GPI_SDIO1_PAD_CCMD_IN 38 +#define GPI_SDIO1_PAD_CDATA_IN_BIT0 39 +#define GPI_SDIO1_PAD_CDATA_IN_BIT1 40 +#define GPI_SDIO1_PAD_CDATA_IN_BIT2 41 +#define GPI_SDIO1_PAD_CDATA_IN_BIT3 42 +#define GPI_SDIO1_PAD_CDATA_IN_BIT4 43 +#define GPI_SDIO1_PAD_CDATA_IN_BIT5 44 +#define GPI_SDIO1_PAD_CDATA_IN_BIT6 45 +#define GPI_SDIO1_PAD_CDATA_IN_BIT7 46 +#define GPI_SPDIF_RX_SDIN 47 +#define GPI_SPI0_PAD_RXD 48 +#define GPI_SPI0_PAD_SS_IN_N 49 +#define GPI_SPI1_PAD_RXD 50 +#define GPI_SPI1_PAD_SS_IN_N 51 +#define GPI_SPI2_PAD_RXD 52 +#define GPI_SPI2_PAD_SS_IN_N 53 +#define GPI_SPI2AHB_PAD_RXD_BIT0 54 +#define GPI_SPI2AHB_PAD_RXD_BIT1 55 +#define GPI_SPI2AHB_PAD_RXD_BIT2 56 +#define GPI_SPI2AHB_PAD_RXD_BIT3 57 +#define GPI_SPI2AHB_PAD_SS_N 58 +#define GPI_SPI2AHB_SLV_SCLKIN 59 +#define GPI_SPI3_PAD_RXD 60 +#define GPI_SPI3_PAD_SS_IN_N 61 +#define GPI_UART0_PAD_CTSN 62 +#define GPI_UART0_PAD_DCDN 63 +#define GPI_UART0_PAD_DSRN 64 +#define GPI_UART0_PAD_RIN 65 +#define GPI_UART0_PAD_SIN 66 +#define GPI_UART1_PAD_SIN 67 +#define GPI_UART2_PAD_CTS_N 68 +#define GPI_UART2_PAD_DCD_N 69 +#define GPI_UART2_PAD_DSR_N 70 +#define GPI_UART2_PAD_RI_N 71 +#define GPI_UART2_PAD_SIN 72 +#define GPI_UART3_PAD_SIN 73 +#define GPI_USB_OVER_CURRENT 74 + +#define GPI_NONE 0xff + +#endif /* __DT_BINDINGS_PINCTRL_STARFIVE_H__ */ From 87998ea9e53d5b00ab8b7d4b42f63317e886894f Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Tue, 27 Jul 2021 14:34:33 +0200 Subject: [PATCH 11/80] dt-bindings: pinctrl: Add StarFive JH7100 bindings commit 7431b391df95f5b8d08fd0f9fa1a75cc038ee290 upstream. Add bindings for the GPIO/pin controller on the JH7100 RISC-V SoC by StarFive Ltd. This is a test chip for their upcoming JH7110 SoC. Reviewed-by: Linus Walleij Reviewed-by: Rob Herring Signed-off-by: Emil Renner Berthing --- .../pinctrl/starfive,jh7100-pinctrl.yaml | 307 ++++++++++++++++++ 1 file changed, 307 insertions(+) create mode 100644 Documentation/devicetree/bindings/pinctrl/starfive,jh7100-pinctrl.yaml diff --git a/Documentation/devicetree/bindings/pinctrl/starfive,jh7100-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/starfive,jh7100-pinctrl.yaml new file mode 100644 index 0000000000000..92963604422f4 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/starfive,jh7100-pinctrl.yaml @@ -0,0 +1,307 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pinctrl/starfive,jh7100-pinctrl.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive JH7100 Pin Controller Device Tree Bindings + +description: | + Bindings for the JH7100 RISC-V SoC from StarFive Ltd. + + Out of the SoC's many pins only the ones named PAD_GPIO[0] to PAD_GPIO[63] + and PAD_FUNC_SHARE[0] to PAD_FUNC_SHARE[141] can be multiplexed and have + configurable bias, drive strength, schmitt trigger etc. The SoC has an + interesting 2-layered approach to pin muxing best illustrated by the diagram + below. + + Signal group 0, 1, ... or 6 + ___|___ + | | + LCD output -----------------| | + CMOS Camera interface ------| |--- PAD_GPIO[0] + Ethernet PHY interface -----| MUX |--- PAD_GPIO[1] + ... | | ... + | |--- PAD_GPIO[63] + -------- GPIO0 ------------| | + | -------|-- GPIO1 --------| |--- PAD_FUNC_SHARE[0] + | | | | | |--- PAD_FUNC_SHARE[1] + | | | | ... | | ... + | | | | | |--- PAD_FUNC_SHARE[141] + | | -----|---|-- GPIO63 ---| | + | | | | | | ------- + UART0 UART1 -- + + + The big MUX in the diagram only has 7 different ways of mapping peripherals + on the left to pins on the right. StarFive calls the 7 configurations "signal + groups". + However some peripherals have their I/O go through the 64 "GPIOs". The + diagram only shows UART0 and UART1, but this also includes a number of other + UARTs, I2Cs, SPIs, PWMs etc. All these peripherals are connected to all 64 + GPIOs such that any GPIO can be set up to be controlled by any of the + peripherals. + Note that signal group 0 doesn't map any of the GPIOs to pins, and only + signal group 1 maps the GPIOs to the pins named PAD_GPIO[0] to PAD_GPIO[63]. + +maintainers: + - Emil Renner Berthing + - Drew Fustini + +properties: + compatible: + const: starfive,jh7100-pinctrl + + reg: + minItems: 2 + maxItems: 2 + + reg-names: + items: + - const: gpio + - const: padctl + + clocks: + maxItems: 1 + + resets: + maxItems: 1 + + gpio-controller: true + + "#gpio-cells": + const: 2 + + interrupts: + maxItems: 1 + description: The GPIO parent interrupt. + + interrupt-controller: true + + "#interrupt-cells": + const: 2 + + starfive,signal-group: + description: | + Select one of the 7 signal groups. If this property is not set it + defaults to the configuration already chosen by the earlier boot stages. + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1, 2, 3, 4, 5, 6] + +required: + - compatible + - reg + - reg-names + - clocks + - gpio-controller + - "#gpio-cells" + - interrupts + - interrupt-controller + - "#interrupt-cells" + +patternProperties: + '-[0-9]+$': + type: object + patternProperties: + '-pins$': + type: object + description: | + A pinctrl node should contain at least one subnode representing the + pinctrl groups available on the machine. Each subnode will list the + pins it needs, and how they should be configured, with regard to + muxer configuration, bias, input enable/disable, input schmitt + trigger enable/disable, slew-rate and drive strength. + $ref: "/schemas/pinctrl/pincfg-node.yaml" + + properties: + pins: + description: | + The list of pin identifiers that properties in the node apply to. + This should be set using either the PAD_GPIO or PAD_FUNC_SHARE + macros. + Either this or "pinmux" has to be specified, but not both. + $ref: "/schemas/pinctrl/pinmux-node.yaml#/properties/pins" + + pinmux: + description: | + The list of GPIOs and their mux settings that properties in the + node apply to. This should be set using the GPIOMUX macro. + Either this or "pins" has to be specified, but not both. + $ref: "/schemas/pinctrl/pinmux-node.yaml#/properties/pinmux" + + bias-disable: true + + bias-pull-up: + type: boolean + + bias-pull-down: + type: boolean + + drive-strength: + enum: [ 14, 21, 28, 35, 42, 49, 56, 63 ] + + input-enable: true + + input-disable: true + + input-schmitt-enable: true + + input-schmitt-disable: true + + slew-rate: + maximum: 7 + + starfive,strong-pull-up: + description: enable strong pull-up. + type: boolean + + additionalProperties: false + + additionalProperties: false + +additionalProperties: false + +examples: + - | + #include + #include + #include + + soc { + #address-cells = <2>; + #size-cells = <2>; + + pinctrl@11910000 { + compatible = "starfive,jh7100-pinctrl"; + reg = <0x0 0x11910000 0x0 0x10000>, + <0x0 0x11858000 0x0 0x1000>; + reg-names = "gpio", "padctl"; + clocks = <&clkgen JH7100_CLK_GPIO_APB>; + resets = <&clkgen JH7100_RSTN_GPIO_APB>; + interrupts = <32>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + starfive,signal-group = <6>; + + gmac_pins_default: gmac-0 { + gtxclk-pins { + pins = ; + bias-pull-up; + drive-strength = <35>; + input-enable; + input-schmitt-enable; + slew-rate = <0>; + }; + miitxclk-pins { + pins = ; + bias-pull-up; + drive-strength = <14>; + input-enable; + input-schmitt-disable; + slew-rate = <0>; + }; + tx-pins { + pins = , + , + , + , + , + , + , + , + ; + bias-disable; + drive-strength = <35>; + input-disable; + input-schmitt-disable; + slew-rate = <0>; + }; + rxclk-pins { + pins = ; + bias-pull-up; + drive-strength = <14>; + input-enable; + input-schmitt-disable; + slew-rate = <6>; + }; + rxer-pins { + pins = ; + bias-pull-up; + drive-strength = <14>; + input-enable; + input-schmitt-disable; + slew-rate = <0>; + }; + rx-pins { + pins = , + , + , + , + , + , + , + , + , + , + , + , + ; + bias-pull-up; + drive-strength = <14>; + input-enable; + input-schmitt-enable; + slew-rate = <0>; + }; + }; + + i2c0_pins_default: i2c0-0 { + i2c-pins { + pinmux = , + ; + bias-disable; /* external pull-up */ + input-enable; + input-schmitt-enable; + }; + }; + + uart3_pins_default: uart3-0 { + rx-pins { + pinmux = ; + bias-pull-up; + input-enable; + input-schmitt-enable; + }; + tx-pins { + pinmux = ; + bias-disable; + input-disable; + input-schmitt-disable; + }; + }; + }; + + gmac { + pinctrl-0 = <&gmac_pins_default>; + pinctrl-names = "default"; + }; + + i2c0 { + pinctrl-0 = <&i2c0_pins_default>; + pinctrl-names = "default"; + }; + + uart3 { + pinctrl-0 = <&uart3_pins_default>; + pinctrl-names = "default"; + }; + }; + +... From 7ca8ecdf27233f556c952a3c46381ac4b0886665 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Tue, 6 Jul 2021 20:19:06 +0200 Subject: [PATCH 12/80] pinctrl: starfive: Add pinctrl driver for StarFive SoCs commit ec648f6b7686b716424e8e73eebb4c11ae199187 upstream. Add a combined pinctrl and GPIO driver for the JH7100 RISC-V SoC by StarFive Ltd. This is a test chip for their upcoming JH7110 SoC, which is said to feature only minor changes to these pinctrl/GPIO parts. For each "GPIO" there are two registers for configuring the output and output enable signals which may come from other peripherals. Among these are two special signals that are constant 0 and constant 1 respectively. Controlling the GPIOs from software is done by choosing one of these signals. In other words the same registers are used for both pin muxing and controlling the GPIOs, which makes it easier to combine the pinctrl and GPIO driver in one. I wrote the pinconf and pinmux parts, but the GPIO part of the code is based on the GPIO driver in the vendor tree written by Huan Feng with cleanups and fixes by Drew and me. Datasheet: https://github.com/starfive-tech/JH7100_Docs/blob/main/JH7100%20Data%20Sheet%20V01.01.04-EN%20(4-21-2021).pdf Reviewed-by: Andy Shevchenko Reviewed-by: Linus Walleij Co-developed-by: Huan Feng Signed-off-by: Huan Feng Co-developed-by: Drew Fustini Signed-off-by: Drew Fustini Signed-off-by: Emil Renner Berthing --- MAINTAINERS | 8 + drivers/pinctrl/Kconfig | 17 + drivers/pinctrl/Makefile | 1 + drivers/pinctrl/pinctrl-starfive.c | 1354 ++++++++++++++++++++++++++++ 4 files changed, 1380 insertions(+) create mode 100644 drivers/pinctrl/pinctrl-starfive.c diff --git a/MAINTAINERS b/MAINTAINERS index 1690a1eca564b..02ade8c318719 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -18163,6 +18163,14 @@ F: Documentation/devicetree/bindings/clock/starfive,jh7100-clkgen.yaml F: drivers/clk/starfive/clk-starfive-jh7100.c F: include/dt-bindings/clock/starfive-jh7100.h +STARFIVE JH7100 PINCTRL DRIVER +M: Emil Renner Berthing +L: linux-gpio@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/pinctrl/starfive,jh7100-pinctrl.yaml +F: drivers/pinctrl/pinctrl-starfive.c +F: include/dt-bindings/pinctrl/pinctrl-starfive.h + STARFIVE JH7100 RESET CONTROLLER DRIVER M: Emil Renner Berthing S: Maintained diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 6a961d5f8726c..0d5b61e4c21e5 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -281,6 +281,23 @@ config PINCTRL_ST select PINCONF select GPIOLIB_IRQCHIP +config PINCTRL_STARFIVE + tristate "Pinctrl and GPIO driver for the StarFive JH7100 SoC" + depends on SOC_STARFIVE || COMPILE_TEST + depends on OF + default SOC_STARFIVE + select GENERIC_PINCTRL_GROUPS + select GENERIC_PINMUX_FUNCTIONS + select GENERIC_PINCONF + select GPIOLIB + select GPIOLIB_IRQCHIP + select OF_GPIO + help + Say yes here to support pin control on the StarFive JH7100 SoC. + This also provides an interface to the GPIO pins not used by other + peripherals supporting inputs, outputs, configuring pull-up/pull-down + and interrupts on input changes. + config PINCTRL_STMFX tristate "STMicroelectronics STMFX GPIO expander pinctrl driver" depends on I2C diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index 5e63de2ffcf41..f5bdd6b209a6f 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_PINCTRL_LANTIQ) += pinctrl-lantiq.o obj-$(CONFIG_PINCTRL_LPC18XX) += pinctrl-lpc18xx.o obj-$(CONFIG_PINCTRL_TB10X) += pinctrl-tb10x.o obj-$(CONFIG_PINCTRL_ST) += pinctrl-st.o +obj-$(CONFIG_PINCTRL_STARFIVE) += pinctrl-starfive.o obj-$(CONFIG_PINCTRL_STMFX) += pinctrl-stmfx.o obj-$(CONFIG_PINCTRL_ZYNQ) += pinctrl-zynq.o obj-$(CONFIG_PINCTRL_ZYNQMP) += pinctrl-zynqmp.o diff --git a/drivers/pinctrl/pinctrl-starfive.c b/drivers/pinctrl/pinctrl-starfive.c new file mode 100644 index 0000000000000..0b912152a405a --- /dev/null +++ b/drivers/pinctrl/pinctrl-starfive.c @@ -0,0 +1,1354 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Pinctrl / GPIO driver for StarFive JH7100 SoC + * + * Copyright (C) 2020 Shanghai StarFive Technology Co., Ltd. + * Copyright (C) 2021 Emil Renner Berthing + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "core.h" +#include "pinctrl-utils.h" +#include "pinmux.h" +#include "pinconf.h" + +#define DRIVER_NAME "pinctrl-starfive" + +/* + * Refer to Section 12. GPIO Registers in the JH7100 data sheet: + * https://github.com/starfive-tech/JH7100_Docs + */ +#define NR_GPIOS 64 + +/* + * Global enable for GPIO interrupts. If bit 0 is set to 1 the GPIO interrupts + * are enabled. If set to 0 the GPIO interrupts are disabled. + */ +#define GPIOEN 0x000 + +/* + * The following 32-bit registers come in pairs, but only the offset of the + * first register is defined. The first controls (interrupts for) GPIO 0-31 and + * the second GPIO 32-63. + */ + +/* + * Interrupt Type. If set to 1 the interrupt is edge-triggered. If set to 0 the + * interrupt is level-triggered. + */ +#define GPIOIS 0x010 + +/* + * Edge-Trigger Interrupt Type. If set to 1 the interrupt gets triggered on + * both positive and negative edges. If set to 0 the interrupt is triggered by a + * single edge. + */ +#define GPIOIBE 0x018 + +/* + * Interrupt Trigger Polarity. If set to 1 the interrupt is triggered on a + * rising edge (edge-triggered) or high level (level-triggered). If set to 0 the + * interrupt is triggered on a falling edge (edge-triggered) or low level + * (level-triggered). + */ +#define GPIOIEV 0x020 + +/* + * Interrupt Mask. If set to 1 the interrupt is enabled (unmasked). If set to 0 + * the interrupt is disabled (masked). Note that the current documentation is + * wrong and says the exct opposite of this. + */ +#define GPIOIE 0x028 + +/* + * Clear Edge-Triggered Interrupts. Write a 1 to clear the edge-triggered + * interrupt. + */ +#define GPIOIC 0x030 + +/* + * Edge-Triggered Interrupt Status. A 1 means the configured edge was detected. + */ +#define GPIORIS 0x038 + +/* + * Interrupt Status after Masking. A 1 means the configured edge or level was + * detected and not masked. + */ +#define GPIOMIS 0x040 + +/* + * Data Value. Dynamically reflects the value of the GPIO pin. If 1 the pin is + * a digital 1 and if 0 the pin is a digital 0. + */ +#define GPIODIN 0x048 + +/* + * From the data sheet section 12.2, there are 64 32-bit output data registers + * and 64 output enable registers. Output data and output enable registers for + * a given GPIO are contiguous. Eg. GPO0_DOUT_CFG is 0x50 and GPO0_DOEN_CFG is + * 0x54 while GPO1_DOUT_CFG is 0x58 and GPO1_DOEN_CFG is 0x5c. The stride + * between GPIO registers is effectively 8, thus: GPOn_DOUT_CFG is 0x50 + 8n + * and GPOn_DOEN_CFG is 0x54 + 8n. + */ +#define GPON_DOUT_CFG 0x050 +#define GPON_DOEN_CFG 0x054 + +/* + * From Section 12.3, there are 75 input signal configuration registers which + * are 4 bytes wide starting with GPI_CPU_JTAG_TCK_CFG at 0x250 and ending with + * GPI_USB_OVER_CURRENT_CFG 0x378 + */ +#define GPI_CFG_OFFSET 0x250 + +/* + * Pad Control Bits. There are 16 pad control bits for each pin located in 103 + * 32-bit registers controlling PAD_GPIO[0] to PAD_GPIO[63] followed by + * PAD_FUNC_SHARE[0] to PAD_FUNC_SHARE[141]. Odd numbered pins use the upper 16 + * bit of each register. + */ +#define PAD_SLEW_RATE_MASK GENMASK(11, 9) +#define PAD_SLEW_RATE_POS 9 +#define PAD_BIAS_STRONG_PULL_UP BIT(8) +#define PAD_INPUT_ENABLE BIT(7) +#define PAD_INPUT_SCHMITT_ENABLE BIT(6) +#define PAD_BIAS_DISABLE BIT(5) +#define PAD_BIAS_PULL_DOWN BIT(4) +#define PAD_BIAS_MASK \ + (PAD_BIAS_STRONG_PULL_UP | \ + PAD_BIAS_DISABLE | \ + PAD_BIAS_PULL_DOWN) +#define PAD_DRIVE_STRENGTH_MASK GENMASK(3, 0) +#define PAD_DRIVE_STRENGTH_POS 0 + +/* + * From Section 11, the IO_PADSHARE_SEL register can be programmed to select + * one of seven pre-defined multiplexed signal groups on PAD_FUNC_SHARE and + * PAD_GPIO pads. This is a global setting. + */ +#define IO_PADSHARE_SEL 0x1a0 + +/* + * This just needs to be some number such that when + * sfp->gpio.pin_base = PAD_INVALID_GPIO then + * starfive_pin_to_gpio(sfp, validpin) is never a valid GPIO number. + * That is it should underflow and return something >= NR_GPIOS. + */ +#define PAD_INVALID_GPIO 0x10000 + +/* + * The packed pinmux values from the device tree look like this: + * + * | 31 - 24 | 23 - 16 | 15 - 8 | 7 | 6 | 5 - 0 | + * | dout | doen | din | dout rev | doen rev | gpio nr | + * + * ..but the GPOn_DOUT_CFG and GPOn_DOEN_CFG registers look like this: + * + * | 31 | 30 - 8 | 7 - 0 | + * | dout/doen rev | unused | dout/doen | + */ +static unsigned int starfive_pinmux_to_gpio(u32 v) +{ + return v & (NR_GPIOS - 1); +} + +static u32 starfive_pinmux_to_dout(u32 v) +{ + return ((v & BIT(7)) << (31 - 7)) | ((v >> 24) & GENMASK(7, 0)); +} + +static u32 starfive_pinmux_to_doen(u32 v) +{ + return ((v & BIT(6)) << (31 - 6)) | ((v >> 16) & GENMASK(7, 0)); +} + +static u32 starfive_pinmux_to_din(u32 v) +{ + return (v >> 8) & GENMASK(7, 0); +} + +/* + * The maximum GPIO output current depends on the chosen drive strength: + * + * DS: 0 1 2 3 4 5 6 7 + * mA: 14.2 21.2 28.2 35.2 42.2 49.1 56.0 62.8 + * + * After rounding that is 7*DS + 14 mA + */ +static u32 starfive_drive_strength_to_max_mA(u16 ds) +{ + return 7 * ds + 14; +} + +static u16 starfive_drive_strength_from_max_mA(u32 i) +{ + return (clamp(i, 14U, 63U) - 14) / 7; +} + +struct starfive_pinctrl { + struct gpio_chip gc; + struct pinctrl_gpio_range gpios; + raw_spinlock_t lock; + void __iomem *base; + void __iomem *padctl; + struct pinctrl_dev *pctl; +}; + +static inline unsigned int starfive_pin_to_gpio(const struct starfive_pinctrl *sfp, + unsigned int pin) +{ + return pin - sfp->gpios.pin_base; +} + +static inline unsigned int starfive_gpio_to_pin(const struct starfive_pinctrl *sfp, + unsigned int gpio) +{ + return sfp->gpios.pin_base + gpio; +} + +static struct starfive_pinctrl *starfive_from_irq_data(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + + return container_of(gc, struct starfive_pinctrl, gc); +} + +static struct starfive_pinctrl *starfive_from_irq_desc(struct irq_desc *desc) +{ + struct gpio_chip *gc = irq_desc_get_handler_data(desc); + + return container_of(gc, struct starfive_pinctrl, gc); +} + +static const struct pinctrl_pin_desc starfive_pins[] = { + PINCTRL_PIN(PAD_GPIO(0), "GPIO[0]"), + PINCTRL_PIN(PAD_GPIO(1), "GPIO[1]"), + PINCTRL_PIN(PAD_GPIO(2), "GPIO[2]"), + PINCTRL_PIN(PAD_GPIO(3), "GPIO[3]"), + PINCTRL_PIN(PAD_GPIO(4), "GPIO[4]"), + PINCTRL_PIN(PAD_GPIO(5), "GPIO[5]"), + PINCTRL_PIN(PAD_GPIO(6), "GPIO[6]"), + PINCTRL_PIN(PAD_GPIO(7), "GPIO[7]"), + PINCTRL_PIN(PAD_GPIO(8), "GPIO[8]"), + PINCTRL_PIN(PAD_GPIO(9), "GPIO[9]"), + PINCTRL_PIN(PAD_GPIO(10), "GPIO[10]"), + PINCTRL_PIN(PAD_GPIO(11), "GPIO[11]"), + PINCTRL_PIN(PAD_GPIO(12), "GPIO[12]"), + PINCTRL_PIN(PAD_GPIO(13), "GPIO[13]"), + PINCTRL_PIN(PAD_GPIO(14), "GPIO[14]"), + PINCTRL_PIN(PAD_GPIO(15), "GPIO[15]"), + PINCTRL_PIN(PAD_GPIO(16), "GPIO[16]"), + PINCTRL_PIN(PAD_GPIO(17), "GPIO[17]"), + PINCTRL_PIN(PAD_GPIO(18), "GPIO[18]"), + PINCTRL_PIN(PAD_GPIO(19), "GPIO[19]"), + PINCTRL_PIN(PAD_GPIO(20), "GPIO[20]"), + PINCTRL_PIN(PAD_GPIO(21), "GPIO[21]"), + PINCTRL_PIN(PAD_GPIO(22), "GPIO[22]"), + PINCTRL_PIN(PAD_GPIO(23), "GPIO[23]"), + PINCTRL_PIN(PAD_GPIO(24), "GPIO[24]"), + PINCTRL_PIN(PAD_GPIO(25), "GPIO[25]"), + PINCTRL_PIN(PAD_GPIO(26), "GPIO[26]"), + PINCTRL_PIN(PAD_GPIO(27), "GPIO[27]"), + PINCTRL_PIN(PAD_GPIO(28), "GPIO[28]"), + PINCTRL_PIN(PAD_GPIO(29), "GPIO[29]"), + PINCTRL_PIN(PAD_GPIO(30), "GPIO[30]"), + PINCTRL_PIN(PAD_GPIO(31), "GPIO[31]"), + PINCTRL_PIN(PAD_GPIO(32), "GPIO[32]"), + PINCTRL_PIN(PAD_GPIO(33), "GPIO[33]"), + PINCTRL_PIN(PAD_GPIO(34), "GPIO[34]"), + PINCTRL_PIN(PAD_GPIO(35), "GPIO[35]"), + PINCTRL_PIN(PAD_GPIO(36), "GPIO[36]"), + PINCTRL_PIN(PAD_GPIO(37), "GPIO[37]"), + PINCTRL_PIN(PAD_GPIO(38), "GPIO[38]"), + PINCTRL_PIN(PAD_GPIO(39), "GPIO[39]"), + PINCTRL_PIN(PAD_GPIO(40), "GPIO[40]"), + PINCTRL_PIN(PAD_GPIO(41), "GPIO[41]"), + PINCTRL_PIN(PAD_GPIO(42), "GPIO[42]"), + PINCTRL_PIN(PAD_GPIO(43), "GPIO[43]"), + PINCTRL_PIN(PAD_GPIO(44), "GPIO[44]"), + PINCTRL_PIN(PAD_GPIO(45), "GPIO[45]"), + PINCTRL_PIN(PAD_GPIO(46), "GPIO[46]"), + PINCTRL_PIN(PAD_GPIO(47), "GPIO[47]"), + PINCTRL_PIN(PAD_GPIO(48), "GPIO[48]"), + PINCTRL_PIN(PAD_GPIO(49), "GPIO[49]"), + PINCTRL_PIN(PAD_GPIO(50), "GPIO[50]"), + PINCTRL_PIN(PAD_GPIO(51), "GPIO[51]"), + PINCTRL_PIN(PAD_GPIO(52), "GPIO[52]"), + PINCTRL_PIN(PAD_GPIO(53), "GPIO[53]"), + PINCTRL_PIN(PAD_GPIO(54), "GPIO[54]"), + PINCTRL_PIN(PAD_GPIO(55), "GPIO[55]"), + PINCTRL_PIN(PAD_GPIO(56), "GPIO[56]"), + PINCTRL_PIN(PAD_GPIO(57), "GPIO[57]"), + PINCTRL_PIN(PAD_GPIO(58), "GPIO[58]"), + PINCTRL_PIN(PAD_GPIO(59), "GPIO[59]"), + PINCTRL_PIN(PAD_GPIO(60), "GPIO[60]"), + PINCTRL_PIN(PAD_GPIO(61), "GPIO[61]"), + PINCTRL_PIN(PAD_GPIO(62), "GPIO[62]"), + PINCTRL_PIN(PAD_GPIO(63), "GPIO[63]"), + PINCTRL_PIN(PAD_FUNC_SHARE(0), "FUNC_SHARE[0]"), + PINCTRL_PIN(PAD_FUNC_SHARE(1), "FUNC_SHARE[1]"), + PINCTRL_PIN(PAD_FUNC_SHARE(2), "FUNC_SHARE[2]"), + PINCTRL_PIN(PAD_FUNC_SHARE(3), "FUNC_SHARE[3]"), + PINCTRL_PIN(PAD_FUNC_SHARE(4), "FUNC_SHARE[4]"), + PINCTRL_PIN(PAD_FUNC_SHARE(5), "FUNC_SHARE[5]"), + PINCTRL_PIN(PAD_FUNC_SHARE(6), "FUNC_SHARE[6]"), + PINCTRL_PIN(PAD_FUNC_SHARE(7), "FUNC_SHARE[7]"), + PINCTRL_PIN(PAD_FUNC_SHARE(8), "FUNC_SHARE[8]"), + PINCTRL_PIN(PAD_FUNC_SHARE(9), "FUNC_SHARE[9]"), + PINCTRL_PIN(PAD_FUNC_SHARE(10), "FUNC_SHARE[10]"), + PINCTRL_PIN(PAD_FUNC_SHARE(11), "FUNC_SHARE[11]"), + PINCTRL_PIN(PAD_FUNC_SHARE(12), "FUNC_SHARE[12]"), + PINCTRL_PIN(PAD_FUNC_SHARE(13), "FUNC_SHARE[13]"), + PINCTRL_PIN(PAD_FUNC_SHARE(14), "FUNC_SHARE[14]"), + PINCTRL_PIN(PAD_FUNC_SHARE(15), "FUNC_SHARE[15]"), + PINCTRL_PIN(PAD_FUNC_SHARE(16), "FUNC_SHARE[16]"), + PINCTRL_PIN(PAD_FUNC_SHARE(17), "FUNC_SHARE[17]"), + PINCTRL_PIN(PAD_FUNC_SHARE(18), "FUNC_SHARE[18]"), + PINCTRL_PIN(PAD_FUNC_SHARE(19), "FUNC_SHARE[19]"), + PINCTRL_PIN(PAD_FUNC_SHARE(20), "FUNC_SHARE[20]"), + PINCTRL_PIN(PAD_FUNC_SHARE(21), "FUNC_SHARE[21]"), + PINCTRL_PIN(PAD_FUNC_SHARE(22), "FUNC_SHARE[22]"), + PINCTRL_PIN(PAD_FUNC_SHARE(23), "FUNC_SHARE[23]"), + PINCTRL_PIN(PAD_FUNC_SHARE(24), "FUNC_SHARE[24]"), + PINCTRL_PIN(PAD_FUNC_SHARE(25), "FUNC_SHARE[25]"), + PINCTRL_PIN(PAD_FUNC_SHARE(26), "FUNC_SHARE[26]"), + PINCTRL_PIN(PAD_FUNC_SHARE(27), "FUNC_SHARE[27]"), + PINCTRL_PIN(PAD_FUNC_SHARE(28), "FUNC_SHARE[28]"), + PINCTRL_PIN(PAD_FUNC_SHARE(29), "FUNC_SHARE[29]"), + PINCTRL_PIN(PAD_FUNC_SHARE(30), "FUNC_SHARE[30]"), + PINCTRL_PIN(PAD_FUNC_SHARE(31), "FUNC_SHARE[31]"), + PINCTRL_PIN(PAD_FUNC_SHARE(32), "FUNC_SHARE[32]"), + PINCTRL_PIN(PAD_FUNC_SHARE(33), "FUNC_SHARE[33]"), + PINCTRL_PIN(PAD_FUNC_SHARE(34), "FUNC_SHARE[34]"), + PINCTRL_PIN(PAD_FUNC_SHARE(35), "FUNC_SHARE[35]"), + PINCTRL_PIN(PAD_FUNC_SHARE(36), "FUNC_SHARE[36]"), + PINCTRL_PIN(PAD_FUNC_SHARE(37), "FUNC_SHARE[37]"), + PINCTRL_PIN(PAD_FUNC_SHARE(38), "FUNC_SHARE[38]"), + PINCTRL_PIN(PAD_FUNC_SHARE(39), "FUNC_SHARE[39]"), + PINCTRL_PIN(PAD_FUNC_SHARE(40), "FUNC_SHARE[40]"), + PINCTRL_PIN(PAD_FUNC_SHARE(41), "FUNC_SHARE[41]"), + PINCTRL_PIN(PAD_FUNC_SHARE(42), "FUNC_SHARE[42]"), + PINCTRL_PIN(PAD_FUNC_SHARE(43), "FUNC_SHARE[43]"), + PINCTRL_PIN(PAD_FUNC_SHARE(44), "FUNC_SHARE[44]"), + PINCTRL_PIN(PAD_FUNC_SHARE(45), "FUNC_SHARE[45]"), + PINCTRL_PIN(PAD_FUNC_SHARE(46), "FUNC_SHARE[46]"), + PINCTRL_PIN(PAD_FUNC_SHARE(47), "FUNC_SHARE[47]"), + PINCTRL_PIN(PAD_FUNC_SHARE(48), "FUNC_SHARE[48]"), + PINCTRL_PIN(PAD_FUNC_SHARE(49), "FUNC_SHARE[49]"), + PINCTRL_PIN(PAD_FUNC_SHARE(50), "FUNC_SHARE[50]"), + PINCTRL_PIN(PAD_FUNC_SHARE(51), "FUNC_SHARE[51]"), + PINCTRL_PIN(PAD_FUNC_SHARE(52), "FUNC_SHARE[52]"), + PINCTRL_PIN(PAD_FUNC_SHARE(53), "FUNC_SHARE[53]"), + PINCTRL_PIN(PAD_FUNC_SHARE(54), "FUNC_SHARE[54]"), + PINCTRL_PIN(PAD_FUNC_SHARE(55), "FUNC_SHARE[55]"), + PINCTRL_PIN(PAD_FUNC_SHARE(56), "FUNC_SHARE[56]"), + PINCTRL_PIN(PAD_FUNC_SHARE(57), "FUNC_SHARE[57]"), + PINCTRL_PIN(PAD_FUNC_SHARE(58), "FUNC_SHARE[58]"), + PINCTRL_PIN(PAD_FUNC_SHARE(59), "FUNC_SHARE[59]"), + PINCTRL_PIN(PAD_FUNC_SHARE(60), "FUNC_SHARE[60]"), + PINCTRL_PIN(PAD_FUNC_SHARE(61), "FUNC_SHARE[61]"), + PINCTRL_PIN(PAD_FUNC_SHARE(62), "FUNC_SHARE[62]"), + PINCTRL_PIN(PAD_FUNC_SHARE(63), "FUNC_SHARE[63]"), + PINCTRL_PIN(PAD_FUNC_SHARE(64), "FUNC_SHARE[64]"), + PINCTRL_PIN(PAD_FUNC_SHARE(65), "FUNC_SHARE[65]"), + PINCTRL_PIN(PAD_FUNC_SHARE(66), "FUNC_SHARE[66]"), + PINCTRL_PIN(PAD_FUNC_SHARE(67), "FUNC_SHARE[67]"), + PINCTRL_PIN(PAD_FUNC_SHARE(68), "FUNC_SHARE[68]"), + PINCTRL_PIN(PAD_FUNC_SHARE(69), "FUNC_SHARE[69]"), + PINCTRL_PIN(PAD_FUNC_SHARE(70), "FUNC_SHARE[70]"), + PINCTRL_PIN(PAD_FUNC_SHARE(71), "FUNC_SHARE[71]"), + PINCTRL_PIN(PAD_FUNC_SHARE(72), "FUNC_SHARE[72]"), + PINCTRL_PIN(PAD_FUNC_SHARE(73), "FUNC_SHARE[73]"), + PINCTRL_PIN(PAD_FUNC_SHARE(74), "FUNC_SHARE[74]"), + PINCTRL_PIN(PAD_FUNC_SHARE(75), "FUNC_SHARE[75]"), + PINCTRL_PIN(PAD_FUNC_SHARE(76), "FUNC_SHARE[76]"), + PINCTRL_PIN(PAD_FUNC_SHARE(77), "FUNC_SHARE[77]"), + PINCTRL_PIN(PAD_FUNC_SHARE(78), "FUNC_SHARE[78]"), + PINCTRL_PIN(PAD_FUNC_SHARE(79), "FUNC_SHARE[79]"), + PINCTRL_PIN(PAD_FUNC_SHARE(80), "FUNC_SHARE[80]"), + PINCTRL_PIN(PAD_FUNC_SHARE(81), "FUNC_SHARE[81]"), + PINCTRL_PIN(PAD_FUNC_SHARE(82), "FUNC_SHARE[82]"), + PINCTRL_PIN(PAD_FUNC_SHARE(83), "FUNC_SHARE[83]"), + PINCTRL_PIN(PAD_FUNC_SHARE(84), "FUNC_SHARE[84]"), + PINCTRL_PIN(PAD_FUNC_SHARE(85), "FUNC_SHARE[85]"), + PINCTRL_PIN(PAD_FUNC_SHARE(86), "FUNC_SHARE[86]"), + PINCTRL_PIN(PAD_FUNC_SHARE(87), "FUNC_SHARE[87]"), + PINCTRL_PIN(PAD_FUNC_SHARE(88), "FUNC_SHARE[88]"), + PINCTRL_PIN(PAD_FUNC_SHARE(89), "FUNC_SHARE[89]"), + PINCTRL_PIN(PAD_FUNC_SHARE(90), "FUNC_SHARE[90]"), + PINCTRL_PIN(PAD_FUNC_SHARE(91), "FUNC_SHARE[91]"), + PINCTRL_PIN(PAD_FUNC_SHARE(92), "FUNC_SHARE[92]"), + PINCTRL_PIN(PAD_FUNC_SHARE(93), "FUNC_SHARE[93]"), + PINCTRL_PIN(PAD_FUNC_SHARE(94), "FUNC_SHARE[94]"), + PINCTRL_PIN(PAD_FUNC_SHARE(95), "FUNC_SHARE[95]"), + PINCTRL_PIN(PAD_FUNC_SHARE(96), "FUNC_SHARE[96]"), + PINCTRL_PIN(PAD_FUNC_SHARE(97), "FUNC_SHARE[97]"), + PINCTRL_PIN(PAD_FUNC_SHARE(98), "FUNC_SHARE[98]"), + PINCTRL_PIN(PAD_FUNC_SHARE(99), "FUNC_SHARE[99]"), + PINCTRL_PIN(PAD_FUNC_SHARE(100), "FUNC_SHARE[100]"), + PINCTRL_PIN(PAD_FUNC_SHARE(101), "FUNC_SHARE[101]"), + PINCTRL_PIN(PAD_FUNC_SHARE(102), "FUNC_SHARE[102]"), + PINCTRL_PIN(PAD_FUNC_SHARE(103), "FUNC_SHARE[103]"), + PINCTRL_PIN(PAD_FUNC_SHARE(104), "FUNC_SHARE[104]"), + PINCTRL_PIN(PAD_FUNC_SHARE(105), "FUNC_SHARE[105]"), + PINCTRL_PIN(PAD_FUNC_SHARE(106), "FUNC_SHARE[106]"), + PINCTRL_PIN(PAD_FUNC_SHARE(107), "FUNC_SHARE[107]"), + PINCTRL_PIN(PAD_FUNC_SHARE(108), "FUNC_SHARE[108]"), + PINCTRL_PIN(PAD_FUNC_SHARE(109), "FUNC_SHARE[109]"), + PINCTRL_PIN(PAD_FUNC_SHARE(110), "FUNC_SHARE[110]"), + PINCTRL_PIN(PAD_FUNC_SHARE(111), "FUNC_SHARE[111]"), + PINCTRL_PIN(PAD_FUNC_SHARE(112), "FUNC_SHARE[112]"), + PINCTRL_PIN(PAD_FUNC_SHARE(113), "FUNC_SHARE[113]"), + PINCTRL_PIN(PAD_FUNC_SHARE(114), "FUNC_SHARE[114]"), + PINCTRL_PIN(PAD_FUNC_SHARE(115), "FUNC_SHARE[115]"), + PINCTRL_PIN(PAD_FUNC_SHARE(116), "FUNC_SHARE[116]"), + PINCTRL_PIN(PAD_FUNC_SHARE(117), "FUNC_SHARE[117]"), + PINCTRL_PIN(PAD_FUNC_SHARE(118), "FUNC_SHARE[118]"), + PINCTRL_PIN(PAD_FUNC_SHARE(119), "FUNC_SHARE[119]"), + PINCTRL_PIN(PAD_FUNC_SHARE(120), "FUNC_SHARE[120]"), + PINCTRL_PIN(PAD_FUNC_SHARE(121), "FUNC_SHARE[121]"), + PINCTRL_PIN(PAD_FUNC_SHARE(122), "FUNC_SHARE[122]"), + PINCTRL_PIN(PAD_FUNC_SHARE(123), "FUNC_SHARE[123]"), + PINCTRL_PIN(PAD_FUNC_SHARE(124), "FUNC_SHARE[124]"), + PINCTRL_PIN(PAD_FUNC_SHARE(125), "FUNC_SHARE[125]"), + PINCTRL_PIN(PAD_FUNC_SHARE(126), "FUNC_SHARE[126]"), + PINCTRL_PIN(PAD_FUNC_SHARE(127), "FUNC_SHARE[127]"), + PINCTRL_PIN(PAD_FUNC_SHARE(128), "FUNC_SHARE[128]"), + PINCTRL_PIN(PAD_FUNC_SHARE(129), "FUNC_SHARE[129]"), + PINCTRL_PIN(PAD_FUNC_SHARE(130), "FUNC_SHARE[130]"), + PINCTRL_PIN(PAD_FUNC_SHARE(131), "FUNC_SHARE[131]"), + PINCTRL_PIN(PAD_FUNC_SHARE(132), "FUNC_SHARE[132]"), + PINCTRL_PIN(PAD_FUNC_SHARE(133), "FUNC_SHARE[133]"), + PINCTRL_PIN(PAD_FUNC_SHARE(134), "FUNC_SHARE[134]"), + PINCTRL_PIN(PAD_FUNC_SHARE(135), "FUNC_SHARE[135]"), + PINCTRL_PIN(PAD_FUNC_SHARE(136), "FUNC_SHARE[136]"), + PINCTRL_PIN(PAD_FUNC_SHARE(137), "FUNC_SHARE[137]"), + PINCTRL_PIN(PAD_FUNC_SHARE(138), "FUNC_SHARE[138]"), + PINCTRL_PIN(PAD_FUNC_SHARE(139), "FUNC_SHARE[139]"), + PINCTRL_PIN(PAD_FUNC_SHARE(140), "FUNC_SHARE[140]"), + PINCTRL_PIN(PAD_FUNC_SHARE(141), "FUNC_SHARE[141]"), +}; + +#ifdef CONFIG_DEBUG_FS +static void starfive_pin_dbg_show(struct pinctrl_dev *pctldev, + struct seq_file *s, + unsigned int pin) +{ + struct starfive_pinctrl *sfp = pinctrl_dev_get_drvdata(pctldev); + unsigned int gpio = starfive_pin_to_gpio(sfp, pin); + void __iomem *reg; + u32 dout, doen; + + if (gpio >= NR_GPIOS) + return; + + reg = sfp->base + GPON_DOUT_CFG + 8 * gpio; + dout = readl_relaxed(reg + 0x000); + doen = readl_relaxed(reg + 0x004); + + seq_printf(s, "dout=%lu%s doen=%lu%s", + dout & GENMASK(7, 0), (dout & BIT(31)) ? "r" : "", + doen & GENMASK(7, 0), (doen & BIT(31)) ? "r" : ""); +} +#else +#define starfive_pin_dbg_show NULL +#endif + +static int starfive_dt_node_to_map(struct pinctrl_dev *pctldev, + struct device_node *np, + struct pinctrl_map **maps, + unsigned int *num_maps) +{ + struct starfive_pinctrl *sfp = pinctrl_dev_get_drvdata(pctldev); + struct device *dev = sfp->gc.parent; + struct device_node *child; + struct pinctrl_map *map; + const char **pgnames; + const char *grpname; + u32 *pinmux; + int ngroups; + int *pins; + int nmaps; + int ret; + + nmaps = 0; + ngroups = 0; + for_each_child_of_node(np, child) { + int npinmux = of_property_count_u32_elems(child, "pinmux"); + int npins = of_property_count_u32_elems(child, "pins"); + + if (npinmux > 0 && npins > 0) { + dev_err(dev, "invalid pinctrl group %pOFn.%pOFn: both pinmux and pins set\n", + np, child); + of_node_put(child); + return -EINVAL; + } + if (npinmux == 0 && npins == 0) { + dev_err(dev, "invalid pinctrl group %pOFn.%pOFn: neither pinmux nor pins set\n", + np, child); + of_node_put(child); + return -EINVAL; + } + + if (npinmux > 0) + nmaps += 2; + else + nmaps += 1; + ngroups += 1; + } + + pgnames = devm_kcalloc(dev, ngroups, sizeof(*pgnames), GFP_KERNEL); + if (!pgnames) + return -ENOMEM; + + map = kcalloc(nmaps, sizeof(*map), GFP_KERNEL); + if (!map) + return -ENOMEM; + + nmaps = 0; + ngroups = 0; + for_each_child_of_node(np, child) { + int npins; + int i; + + grpname = devm_kasprintf(dev, GFP_KERNEL, "%pOFn.%pOFn", np, child); + if (!grpname) { + ret = -ENOMEM; + goto put_child; + } + + pgnames[ngroups++] = grpname; + + if ((npins = of_property_count_u32_elems(child, "pinmux")) > 0) { + pins = devm_kcalloc(dev, npins, sizeof(*pins), GFP_KERNEL); + if (!pins) { + ret = -ENOMEM; + goto put_child; + } + + pinmux = devm_kcalloc(dev, npins, sizeof(*pinmux), GFP_KERNEL); + if (!pinmux) { + ret = -ENOMEM; + goto put_child; + } + + ret = of_property_read_u32_array(child, "pinmux", pinmux, npins); + if (ret) + goto put_child; + + for (i = 0; i < npins; i++) { + unsigned int gpio = starfive_pinmux_to_gpio(pinmux[i]); + + pins[i] = starfive_gpio_to_pin(sfp, gpio); + } + + map[nmaps].type = PIN_MAP_TYPE_MUX_GROUP; + map[nmaps].data.mux.function = np->name; + map[nmaps].data.mux.group = grpname; + nmaps += 1; + } else if ((npins = of_property_count_u32_elems(child, "pins")) > 0) { + pins = devm_kcalloc(dev, npins, sizeof(*pins), GFP_KERNEL); + if (!pins) { + ret = -ENOMEM; + goto put_child; + } + + pinmux = NULL; + + for (i = 0; i < npins; i++) { + u32 v; + + ret = of_property_read_u32_index(child, "pins", i, &v); + if (ret) + goto put_child; + pins[i] = v; + } + } else { + ret = -EINVAL; + goto put_child; + } + + ret = pinctrl_generic_add_group(pctldev, grpname, pins, npins, pinmux); + if (ret < 0) { + dev_err(dev, "error adding group %s: %d\n", grpname, ret); + goto put_child; + } + + ret = pinconf_generic_parse_dt_config(child, pctldev, + &map[nmaps].data.configs.configs, + &map[nmaps].data.configs.num_configs); + if (ret) { + dev_err(dev, "error parsing pin config of group %s: %d\n", + grpname, ret); + goto put_child; + } + + /* don't create a map if there are no pinconf settings */ + if (map[nmaps].data.configs.num_configs == 0) + continue; + + map[nmaps].type = PIN_MAP_TYPE_CONFIGS_GROUP; + map[nmaps].data.configs.group_or_pin = grpname; + nmaps += 1; + } + + ret = pinmux_generic_add_function(pctldev, np->name, pgnames, ngroups, NULL); + if (ret < 0) { + dev_err(dev, "error adding function %s: %d\n", np->name, ret); + goto free_map; + } + + *maps = map; + *num_maps = nmaps; + return 0; + +put_child: + of_node_put(child); +free_map: + pinctrl_utils_free_map(pctldev, map, nmaps); + return ret; +} + +static const struct pinctrl_ops starfive_pinctrl_ops = { + .get_groups_count = pinctrl_generic_get_group_count, + .get_group_name = pinctrl_generic_get_group_name, + .get_group_pins = pinctrl_generic_get_group_pins, + .pin_dbg_show = starfive_pin_dbg_show, + .dt_node_to_map = starfive_dt_node_to_map, + .dt_free_map = pinctrl_utils_free_map, +}; + +static int starfive_set_mux(struct pinctrl_dev *pctldev, + unsigned int fsel, unsigned int gsel) +{ + struct starfive_pinctrl *sfp = pinctrl_dev_get_drvdata(pctldev); + struct device *dev = sfp->gc.parent; + const struct group_desc *group; + const u32 *pinmux; + unsigned int i; + + group = pinctrl_generic_get_group(pctldev, gsel); + if (!group) + return -EINVAL; + + pinmux = group->data; + for (i = 0; i < group->num_pins; i++) { + u32 v = pinmux[i]; + unsigned int gpio = starfive_pinmux_to_gpio(v); + u32 dout = starfive_pinmux_to_dout(v); + u32 doen = starfive_pinmux_to_doen(v); + u32 din = starfive_pinmux_to_din(v); + void __iomem *reg_dout; + void __iomem *reg_doen; + void __iomem *reg_din; + unsigned long flags; + + dev_dbg(dev, "GPIO%u: dout=0x%x doen=0x%x din=0x%x\n", + gpio, dout, doen, din); + + reg_dout = sfp->base + GPON_DOUT_CFG + 8 * gpio; + reg_doen = sfp->base + GPON_DOEN_CFG + 8 * gpio; + if (din != GPI_NONE) + reg_din = sfp->base + GPI_CFG_OFFSET + 4 * din; + else + reg_din = NULL; + + raw_spin_lock_irqsave(&sfp->lock, flags); + writel_relaxed(dout, reg_dout); + writel_relaxed(doen, reg_doen); + if (reg_din) + writel_relaxed(gpio + 2, reg_din); + raw_spin_unlock_irqrestore(&sfp->lock, flags); + } + + return 0; +} + +static const struct pinmux_ops starfive_pinmux_ops = { + .get_functions_count = pinmux_generic_get_function_count, + .get_function_name = pinmux_generic_get_function_name, + .get_function_groups = pinmux_generic_get_function_groups, + .set_mux = starfive_set_mux, + .strict = true, +}; + +static u16 starfive_padctl_get(struct starfive_pinctrl *sfp, + unsigned int pin) +{ + void __iomem *reg = sfp->padctl + 4 * (pin / 2); + int shift = 16 * (pin % 2); + + return readl_relaxed(reg) >> shift; +} + +static void starfive_padctl_rmw(struct starfive_pinctrl *sfp, + unsigned int pin, + u16 _mask, u16 _value) +{ + void __iomem *reg = sfp->padctl + 4 * (pin / 2); + int shift = 16 * (pin % 2); + u32 mask = (u32)_mask << shift; + u32 value = (u32)_value << shift; + unsigned long flags; + + dev_dbg(sfp->gc.parent, "padctl_rmw(%u, 0x%03x, 0x%03x)\n", pin, _mask, _value); + + raw_spin_lock_irqsave(&sfp->lock, flags); + value |= readl_relaxed(reg) & ~mask; + writel_relaxed(value, reg); + raw_spin_unlock_irqrestore(&sfp->lock, flags); +} + +#define PIN_CONFIG_STARFIVE_STRONG_PULL_UP (PIN_CONFIG_END + 1) + +static const struct pinconf_generic_params starfive_pinconf_custom_params[] = { + { "starfive,strong-pull-up", PIN_CONFIG_STARFIVE_STRONG_PULL_UP, 1 }, +}; + +#ifdef CONFIG_DEBUG_FS +static const struct pin_config_item starfive_pinconf_custom_conf_items[] = { + PCONFDUMP(PIN_CONFIG_STARFIVE_STRONG_PULL_UP, "input bias strong pull-up", NULL, false), +}; + +static_assert(ARRAY_SIZE(starfive_pinconf_custom_conf_items) == + ARRAY_SIZE(starfive_pinconf_custom_params)); +#else +#define starfive_pinconf_custom_conf_items NULL +#endif + +static int starfive_pinconf_get(struct pinctrl_dev *pctldev, + unsigned int pin, unsigned long *config) +{ + struct starfive_pinctrl *sfp = pinctrl_dev_get_drvdata(pctldev); + int param = pinconf_to_config_param(*config); + u16 value = starfive_padctl_get(sfp, pin); + bool enabled; + u32 arg; + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + enabled = value & PAD_BIAS_DISABLE; + arg = 0; + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + enabled = value & PAD_BIAS_PULL_DOWN; + arg = 1; + break; + case PIN_CONFIG_BIAS_PULL_UP: + enabled = !(value & PAD_BIAS_MASK); + arg = 1; + break; + case PIN_CONFIG_DRIVE_STRENGTH: + enabled = value & PAD_DRIVE_STRENGTH_MASK; + arg = starfive_drive_strength_to_max_mA(value & PAD_DRIVE_STRENGTH_MASK); + break; + case PIN_CONFIG_INPUT_ENABLE: + enabled = value & PAD_INPUT_ENABLE; + arg = enabled; + break; + case PIN_CONFIG_INPUT_SCHMITT_ENABLE: + enabled = value & PAD_INPUT_SCHMITT_ENABLE; + arg = enabled; + break; + case PIN_CONFIG_SLEW_RATE: + enabled = value & PAD_SLEW_RATE_MASK; + arg = (value & PAD_SLEW_RATE_MASK) >> PAD_SLEW_RATE_POS; + break; + case PIN_CONFIG_STARFIVE_STRONG_PULL_UP: + enabled = value & PAD_BIAS_STRONG_PULL_UP; + arg = enabled; + break; + default: + return -ENOTSUPP; + } + + *config = pinconf_to_config_packed(param, arg); + return enabled ? 0 : -EINVAL; +} + +static int starfive_pinconf_group_get(struct pinctrl_dev *pctldev, + unsigned int gsel, unsigned long *config) +{ + const struct group_desc *group; + + group = pinctrl_generic_get_group(pctldev, gsel); + if (!group) + return -EINVAL; + + return starfive_pinconf_get(pctldev, group->pins[0], config); +} + +static int starfive_pinconf_group_set(struct pinctrl_dev *pctldev, + unsigned int gsel, + unsigned long *configs, + unsigned int num_configs) +{ + struct starfive_pinctrl *sfp = pinctrl_dev_get_drvdata(pctldev); + const struct group_desc *group; + u16 mask, value; + int i; + + group = pinctrl_generic_get_group(pctldev, gsel); + if (!group) + return -EINVAL; + + mask = 0; + value = 0; + for (i = 0; i < num_configs; i++) { + int param = pinconf_to_config_param(configs[i]); + u32 arg = pinconf_to_config_argument(configs[i]); + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + mask |= PAD_BIAS_MASK; + value = (value & ~PAD_BIAS_MASK) | PAD_BIAS_DISABLE; + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + if (arg == 0) + return -ENOTSUPP; + mask |= PAD_BIAS_MASK; + value = (value & ~PAD_BIAS_MASK) | PAD_BIAS_PULL_DOWN; + break; + case PIN_CONFIG_BIAS_PULL_UP: + if (arg == 0) + return -ENOTSUPP; + mask |= PAD_BIAS_MASK; + value = value & ~PAD_BIAS_MASK; + break; + case PIN_CONFIG_DRIVE_STRENGTH: + mask |= PAD_DRIVE_STRENGTH_MASK; + value = (value & ~PAD_DRIVE_STRENGTH_MASK) | + starfive_drive_strength_from_max_mA(arg); + break; + case PIN_CONFIG_INPUT_ENABLE: + mask |= PAD_INPUT_ENABLE; + if (arg) + value |= PAD_INPUT_ENABLE; + else + value &= ~PAD_INPUT_ENABLE; + break; + case PIN_CONFIG_INPUT_SCHMITT_ENABLE: + mask |= PAD_INPUT_SCHMITT_ENABLE; + if (arg) + value |= PAD_INPUT_SCHMITT_ENABLE; + else + value &= ~PAD_INPUT_SCHMITT_ENABLE; + break; + case PIN_CONFIG_SLEW_RATE: + mask |= PAD_SLEW_RATE_MASK; + value = (value & ~PAD_SLEW_RATE_MASK) | + ((arg << PAD_SLEW_RATE_POS) & PAD_SLEW_RATE_MASK); + break; + case PIN_CONFIG_STARFIVE_STRONG_PULL_UP: + if (arg) { + mask |= PAD_BIAS_MASK; + value = (value & ~PAD_BIAS_MASK) | + PAD_BIAS_STRONG_PULL_UP; + } else { + mask |= PAD_BIAS_STRONG_PULL_UP; + value = value & ~PAD_BIAS_STRONG_PULL_UP; + } + break; + default: + return -ENOTSUPP; + } + } + + for (i = 0; i < group->num_pins; i++) + starfive_padctl_rmw(sfp, group->pins[i], mask, value); + + return 0; +} + +#ifdef CONFIG_DEBUG_FS +static void starfive_pinconf_dbg_show(struct pinctrl_dev *pctldev, + struct seq_file *s, unsigned int pin) +{ + struct starfive_pinctrl *sfp = pinctrl_dev_get_drvdata(pctldev); + u16 value = starfive_padctl_get(sfp, pin); + + seq_printf(s, " (0x%03x)", value); +} +#else +#define starfive_pinconf_dbg_show NULL +#endif + +static const struct pinconf_ops starfive_pinconf_ops = { + .pin_config_get = starfive_pinconf_get, + .pin_config_group_get = starfive_pinconf_group_get, + .pin_config_group_set = starfive_pinconf_group_set, + .pin_config_dbg_show = starfive_pinconf_dbg_show, + .is_generic = true, +}; + +static struct pinctrl_desc starfive_desc = { + .name = DRIVER_NAME, + .pins = starfive_pins, + .npins = ARRAY_SIZE(starfive_pins), + .pctlops = &starfive_pinctrl_ops, + .pmxops = &starfive_pinmux_ops, + .confops = &starfive_pinconf_ops, + .owner = THIS_MODULE, + .num_custom_params = ARRAY_SIZE(starfive_pinconf_custom_params), + .custom_params = starfive_pinconf_custom_params, + .custom_conf_items = starfive_pinconf_custom_conf_items, +}; + +static int starfive_gpio_request(struct gpio_chip *gc, unsigned int gpio) +{ + return pinctrl_gpio_request(gc->base + gpio); +} + +static void starfive_gpio_free(struct gpio_chip *gc, unsigned int gpio) +{ + pinctrl_gpio_free(gc->base + gpio); +} + +static int starfive_gpio_get_direction(struct gpio_chip *gc, unsigned int gpio) +{ + struct starfive_pinctrl *sfp = container_of(gc, struct starfive_pinctrl, gc); + void __iomem *doen = sfp->base + GPON_DOEN_CFG + 8 * gpio; + + if (readl_relaxed(doen) == GPO_ENABLE) + return GPIO_LINE_DIRECTION_OUT; + + return GPIO_LINE_DIRECTION_IN; +} + +static int starfive_gpio_direction_input(struct gpio_chip *gc, + unsigned int gpio) +{ + struct starfive_pinctrl *sfp = container_of(gc, struct starfive_pinctrl, gc); + void __iomem *doen = sfp->base + GPON_DOEN_CFG + 8 * gpio; + unsigned long flags; + + /* enable input and schmitt trigger */ + starfive_padctl_rmw(sfp, starfive_gpio_to_pin(sfp, gpio), + PAD_INPUT_ENABLE | PAD_INPUT_SCHMITT_ENABLE, + PAD_INPUT_ENABLE | PAD_INPUT_SCHMITT_ENABLE); + + raw_spin_lock_irqsave(&sfp->lock, flags); + writel_relaxed(GPO_DISABLE, doen); + raw_spin_unlock_irqrestore(&sfp->lock, flags); + return 0; +} + +static int starfive_gpio_direction_output(struct gpio_chip *gc, + unsigned int gpio, int value) +{ + struct starfive_pinctrl *sfp = container_of(gc, struct starfive_pinctrl, gc); + void __iomem *dout = sfp->base + GPON_DOUT_CFG + 8 * gpio; + void __iomem *doen = sfp->base + GPON_DOEN_CFG + 8 * gpio; + unsigned long flags; + + raw_spin_lock_irqsave(&sfp->lock, flags); + writel_relaxed(value, dout); + writel_relaxed(GPO_ENABLE, doen); + raw_spin_unlock_irqrestore(&sfp->lock, flags); + + /* disable input, schmitt trigger and bias */ + starfive_padctl_rmw(sfp, starfive_gpio_to_pin(sfp, gpio), + PAD_BIAS_MASK | PAD_INPUT_ENABLE | PAD_INPUT_SCHMITT_ENABLE, + PAD_BIAS_DISABLE); + + return 0; +} + +static int starfive_gpio_get(struct gpio_chip *gc, unsigned int gpio) +{ + struct starfive_pinctrl *sfp = container_of(gc, struct starfive_pinctrl, gc); + void __iomem *din = sfp->base + GPIODIN + 4 * (gpio / 32); + + return !!(readl_relaxed(din) & BIT(gpio % 32)); +} + +static void starfive_gpio_set(struct gpio_chip *gc, unsigned int gpio, + int value) +{ + struct starfive_pinctrl *sfp = container_of(gc, struct starfive_pinctrl, gc); + void __iomem *dout = sfp->base + GPON_DOUT_CFG + 8 * gpio; + unsigned long flags; + + raw_spin_lock_irqsave(&sfp->lock, flags); + writel_relaxed(value, dout); + raw_spin_unlock_irqrestore(&sfp->lock, flags); +} + +static int starfive_gpio_set_config(struct gpio_chip *gc, unsigned int gpio, + unsigned long config) +{ + struct starfive_pinctrl *sfp = container_of(gc, struct starfive_pinctrl, gc); + u32 arg = pinconf_to_config_argument(config); + u16 value; + u16 mask; + + switch (pinconf_to_config_param(config)) { + case PIN_CONFIG_BIAS_DISABLE: + mask = PAD_BIAS_MASK; + value = PAD_BIAS_DISABLE; + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + if (arg == 0) + return -ENOTSUPP; + mask = PAD_BIAS_MASK; + value = PAD_BIAS_PULL_DOWN; + break; + case PIN_CONFIG_BIAS_PULL_UP: + if (arg == 0) + return -ENOTSUPP; + mask = PAD_BIAS_MASK; + value = 0; + break; + case PIN_CONFIG_DRIVE_PUSH_PULL: + return 0; + case PIN_CONFIG_INPUT_ENABLE: + mask = PAD_INPUT_ENABLE; + value = arg ? PAD_INPUT_ENABLE : 0; + break; + case PIN_CONFIG_INPUT_SCHMITT_ENABLE: + mask = PAD_INPUT_SCHMITT_ENABLE; + value = arg ? PAD_INPUT_SCHMITT_ENABLE : 0; + break; + default: + return -ENOTSUPP; + }; + + starfive_padctl_rmw(sfp, starfive_gpio_to_pin(sfp, gpio), mask, value); + return 0; +} + +static int starfive_gpio_add_pin_ranges(struct gpio_chip *gc) +{ + struct starfive_pinctrl *sfp = container_of(gc, struct starfive_pinctrl, gc); + + sfp->gpios.name = sfp->gc.label; + sfp->gpios.base = sfp->gc.base; + /* + * sfp->gpios.pin_base depends on the chosen signal group + * and is set in starfive_probe() + */ + sfp->gpios.npins = NR_GPIOS; + sfp->gpios.gc = &sfp->gc; + pinctrl_add_gpio_range(sfp->pctl, &sfp->gpios); + return 0; +} + +static void starfive_irq_ack(struct irq_data *d) +{ + struct starfive_pinctrl *sfp = starfive_from_irq_data(d); + irq_hw_number_t gpio = irqd_to_hwirq(d); + void __iomem *ic = sfp->base + GPIOIC + 4 * (gpio / 32); + u32 mask = BIT(gpio % 32); + unsigned long flags; + + raw_spin_lock_irqsave(&sfp->lock, flags); + writel_relaxed(mask, ic); + raw_spin_unlock_irqrestore(&sfp->lock, flags); +} + +static void starfive_irq_mask(struct irq_data *d) +{ + struct starfive_pinctrl *sfp = starfive_from_irq_data(d); + irq_hw_number_t gpio = irqd_to_hwirq(d); + void __iomem *ie = sfp->base + GPIOIE + 4 * (gpio / 32); + u32 mask = BIT(gpio % 32); + unsigned long flags; + u32 value; + + raw_spin_lock_irqsave(&sfp->lock, flags); + value = readl_relaxed(ie) & ~mask; + writel_relaxed(value, ie); + raw_spin_unlock_irqrestore(&sfp->lock, flags); +} + +static void starfive_irq_mask_ack(struct irq_data *d) +{ + struct starfive_pinctrl *sfp = starfive_from_irq_data(d); + irq_hw_number_t gpio = irqd_to_hwirq(d); + void __iomem *ie = sfp->base + GPIOIE + 4 * (gpio / 32); + void __iomem *ic = sfp->base + GPIOIC + 4 * (gpio / 32); + u32 mask = BIT(gpio % 32); + unsigned long flags; + u32 value; + + raw_spin_lock_irqsave(&sfp->lock, flags); + value = readl_relaxed(ie) & ~mask; + writel_relaxed(value, ie); + writel_relaxed(mask, ic); + raw_spin_unlock_irqrestore(&sfp->lock, flags); +} + +static void starfive_irq_unmask(struct irq_data *d) +{ + struct starfive_pinctrl *sfp = starfive_from_irq_data(d); + irq_hw_number_t gpio = irqd_to_hwirq(d); + void __iomem *ie = sfp->base + GPIOIE + 4 * (gpio / 32); + u32 mask = BIT(gpio % 32); + unsigned long flags; + u32 value; + + raw_spin_lock_irqsave(&sfp->lock, flags); + value = readl_relaxed(ie) | mask; + writel_relaxed(value, ie); + raw_spin_unlock_irqrestore(&sfp->lock, flags); +} + +static int starfive_irq_set_type(struct irq_data *d, unsigned int trigger) +{ + struct starfive_pinctrl *sfp = starfive_from_irq_data(d); + irq_hw_number_t gpio = irqd_to_hwirq(d); + void __iomem *base = sfp->base + 4 * (gpio / 32); + u32 mask = BIT(gpio % 32); + u32 irq_type, edge_both, polarity; + unsigned long flags; + + switch (trigger) { + case IRQ_TYPE_EDGE_RISING: + irq_type = mask; /* 1: edge triggered */ + edge_both = 0; /* 0: single edge */ + polarity = mask; /* 1: rising edge */ + break; + case IRQ_TYPE_EDGE_FALLING: + irq_type = mask; /* 1: edge triggered */ + edge_both = 0; /* 0: single edge */ + polarity = 0; /* 0: falling edge */ + break; + case IRQ_TYPE_EDGE_BOTH: + irq_type = mask; /* 1: edge triggered */ + edge_both = mask; /* 1: both edges */ + polarity = 0; /* 0: ignored */ + break; + case IRQ_TYPE_LEVEL_HIGH: + irq_type = 0; /* 0: level triggered */ + edge_both = 0; /* 0: ignored */ + polarity = mask; /* 1: high level */ + break; + case IRQ_TYPE_LEVEL_LOW: + irq_type = 0; /* 0: level triggered */ + edge_both = 0; /* 0: ignored */ + polarity = 0; /* 0: low level */ + break; + default: + return -EINVAL; + } + + if (trigger & IRQ_TYPE_EDGE_BOTH) + irq_set_handler_locked(d, handle_edge_irq); + else + irq_set_handler_locked(d, handle_level_irq); + + raw_spin_lock_irqsave(&sfp->lock, flags); + irq_type |= readl_relaxed(base + GPIOIS) & ~mask; + writel_relaxed(irq_type, base + GPIOIS); + edge_both |= readl_relaxed(base + GPIOIBE) & ~mask; + writel_relaxed(edge_both, base + GPIOIBE); + polarity |= readl_relaxed(base + GPIOIEV) & ~mask; + writel_relaxed(polarity, base + GPIOIEV); + raw_spin_unlock_irqrestore(&sfp->lock, flags); + return 0; +} + +static struct irq_chip starfive_irq_chip = { + .irq_ack = starfive_irq_ack, + .irq_mask = starfive_irq_mask, + .irq_mask_ack = starfive_irq_mask_ack, + .irq_unmask = starfive_irq_unmask, + .irq_set_type = starfive_irq_set_type, + .flags = IRQCHIP_SET_TYPE_MASKED, +}; + +static void starfive_gpio_irq_handler(struct irq_desc *desc) +{ + struct starfive_pinctrl *sfp = starfive_from_irq_desc(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); + unsigned long mis; + unsigned int pin; + + chained_irq_enter(chip, desc); + + mis = readl_relaxed(sfp->base + GPIOMIS + 0); + for_each_set_bit(pin, &mis, 32) + generic_handle_domain_irq(sfp->gc.irq.domain, pin); + + mis = readl_relaxed(sfp->base + GPIOMIS + 4); + for_each_set_bit(pin, &mis, 32) + generic_handle_domain_irq(sfp->gc.irq.domain, pin + 32); + + chained_irq_exit(chip, desc); +} + +static int starfive_gpio_init_hw(struct gpio_chip *gc) +{ + struct starfive_pinctrl *sfp = container_of(gc, struct starfive_pinctrl, gc); + + /* mask all GPIO interrupts */ + writel(0, sfp->base + GPIOIE + 0); + writel(0, sfp->base + GPIOIE + 4); + /* clear edge interrupt flags */ + writel(~0U, sfp->base + GPIOIC + 0); + writel(~0U, sfp->base + GPIOIC + 4); + /* enable GPIO interrupts */ + writel(1, sfp->base + GPIOEN); + return 0; +} + +static void starfive_disable_clock(void *data) +{ + clk_disable_unprepare(data); +} + +static int starfive_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct starfive_pinctrl *sfp; + struct reset_control *rst; + struct clk *clk; + u32 value; + int ret; + + sfp = devm_kzalloc(dev, sizeof(*sfp), GFP_KERNEL); + if (!sfp) + return -ENOMEM; + + sfp->base = devm_platform_ioremap_resource_byname(pdev, "gpio"); + if (IS_ERR(sfp->base)) + return PTR_ERR(sfp->base); + + sfp->padctl = devm_platform_ioremap_resource_byname(pdev, "padctl"); + if (IS_ERR(sfp->padctl)) + return PTR_ERR(sfp->padctl); + + clk = devm_clk_get(dev, NULL); + if (IS_ERR(clk)) + return dev_err_probe(dev, PTR_ERR(clk), "could not get clock\n"); + + rst = devm_reset_control_get_exclusive(dev, NULL); + if (IS_ERR(rst)) + return dev_err_probe(dev, PTR_ERR(rst), "could not get reset\n"); + + ret = clk_prepare_enable(clk); + if (ret) + return dev_err_probe(dev, ret, "could not enable clock\n"); + + ret = devm_add_action_or_reset(dev, starfive_disable_clock, clk); + if (ret) + return ret; + + /* + * We don't want to assert reset and risk undoing pin muxing for the + * early boot serial console, but let's make sure the reset line is + * deasserted in case someone runs a really minimal bootloader. + */ + ret = reset_control_deassert(rst); + if (ret) + return dev_err_probe(dev, ret, "could not deassert reset\n"); + + platform_set_drvdata(pdev, sfp); + sfp->gc.parent = dev; + raw_spin_lock_init(&sfp->lock); + + ret = devm_pinctrl_register_and_init(dev, &starfive_desc, sfp, &sfp->pctl); + if (ret) + return dev_err_probe(dev, ret, "could not register pinctrl driver\n"); + + if (!of_property_read_u32(dev->of_node, "starfive,signal-group", &value)) { + if (value > 6) + return dev_err_probe(dev, -EINVAL, "invalid signal group %u\n", value); + writel(value, sfp->padctl + IO_PADSHARE_SEL); + } + + value = readl(sfp->padctl + IO_PADSHARE_SEL); + switch (value) { + case 0: + sfp->gpios.pin_base = PAD_INVALID_GPIO; + goto out_pinctrl_enable; + case 1: + sfp->gpios.pin_base = PAD_GPIO(0); + break; + case 2: + sfp->gpios.pin_base = PAD_FUNC_SHARE(72); + break; + case 3: + sfp->gpios.pin_base = PAD_FUNC_SHARE(70); + break; + case 4: case 5: case 6: + sfp->gpios.pin_base = PAD_FUNC_SHARE(0); + break; + default: + return dev_err_probe(dev, -EINVAL, "invalid signal group %u\n", value); + } + + sfp->gc.label = dev_name(dev); + sfp->gc.owner = THIS_MODULE; + sfp->gc.request = starfive_gpio_request; + sfp->gc.free = starfive_gpio_free; + sfp->gc.get_direction = starfive_gpio_get_direction; + sfp->gc.direction_input = starfive_gpio_direction_input; + sfp->gc.direction_output = starfive_gpio_direction_output; + sfp->gc.get = starfive_gpio_get; + sfp->gc.set = starfive_gpio_set; + sfp->gc.set_config = starfive_gpio_set_config; + sfp->gc.add_pin_ranges = starfive_gpio_add_pin_ranges; + sfp->gc.base = -1; + sfp->gc.ngpio = NR_GPIOS; + + starfive_irq_chip.parent_device = dev; + starfive_irq_chip.name = sfp->gc.label; + + sfp->gc.irq.chip = &starfive_irq_chip; + sfp->gc.irq.parent_handler = starfive_gpio_irq_handler; + sfp->gc.irq.num_parents = 1; + sfp->gc.irq.parents = devm_kcalloc(dev, sfp->gc.irq.num_parents, + sizeof(*sfp->gc.irq.parents), GFP_KERNEL); + if (!sfp->gc.irq.parents) + return -ENOMEM; + sfp->gc.irq.default_type = IRQ_TYPE_NONE; + sfp->gc.irq.handler = handle_bad_irq; + sfp->gc.irq.init_hw = starfive_gpio_init_hw; + + ret = platform_get_irq(pdev, 0); + if (ret < 0) + return ret; + sfp->gc.irq.parents[0] = ret; + + ret = devm_gpiochip_add_data(dev, &sfp->gc, sfp); + if (ret) + return dev_err_probe(dev, ret, "could not register gpiochip\n"); + +out_pinctrl_enable: + return pinctrl_enable(sfp->pctl); +} + +static const struct of_device_id starfive_of_match[] = { + { .compatible = "starfive,jh7100-pinctrl" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, starfive_of_match); + +static struct platform_driver starfive_pinctrl_driver = { + .probe = starfive_probe, + .driver = { + .name = DRIVER_NAME, + .of_match_table = starfive_of_match, + }, +}; +module_platform_driver(starfive_pinctrl_driver); + +MODULE_DESCRIPTION("Pinctrl driver for StarFive SoCs"); +MODULE_AUTHOR("Emil Renner Berthing "); +MODULE_LICENSE("GPL v2"); From 13b6b785b6a58cd3f6c78870a26c7a57b8eb8482 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Thu, 7 Oct 2021 14:24:29 +0200 Subject: [PATCH 13/80] dt-bindings: serial: snps-dw-apb-uart: Add JH7100 uarts commit d0b65b1500973fef840dbc4bb9f9c237db2b761f upstream. Add compatibles for the StarFive JH7100 uarts. Reviewed-by: Geert Uytterhoeven Acked-by: Rob Herring Signed-off-by: Emil Renner Berthing --- .../devicetree/bindings/serial/snps-dw-apb-uart.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/devicetree/bindings/serial/snps-dw-apb-uart.yaml b/Documentation/devicetree/bindings/serial/snps-dw-apb-uart.yaml index b49fda5e608f1..12137fe80acff 100644 --- a/Documentation/devicetree/bindings/serial/snps-dw-apb-uart.yaml +++ b/Documentation/devicetree/bindings/serial/snps-dw-apb-uart.yaml @@ -40,6 +40,11 @@ properties: - brcm,bcm11351-dw-apb-uart - brcm,bcm21664-dw-apb-uart - const: snps,dw-apb-uart + - items: + - enum: + - starfive,jh7100-hsuart + - starfive,jh7100-uart + - const: snps,dw-apb-uart - const: snps,dw-apb-uart reg: From 967bfb7f05b7eda49ee8f4a5621905a2759a7761 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Mon, 4 Oct 2021 19:40:29 +0200 Subject: [PATCH 14/80] serial: 8250_dw: Add StarFive JH7100 quirk commit b0ad20a3b64bf653a717860819691b262c0b2a2b upstream. On the StarFive JH7100 RISC-V SoC the UART core clocks can't be set to exactly 16 * 115200Hz and many other common bitrates. Trying this will only result in a higher input clock, but low enough that the UART's internal divisor can't come close enough to the baud rate target. So rather than try to set the input clock it's better to skip the clk_set_rate call and rely solely on the UART's internal divisor. Reviewed-by: Andy Shevchenko Reviewed-by: Geert Uytterhoeven Signed-off-by: Emil Renner Berthing --- drivers/tty/serial/8250/8250_dw.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index 53f57c3b9f42c..1769808031c52 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -414,6 +414,8 @@ static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data) if (of_device_is_compatible(np, "marvell,armada-38x-uart")) p->serial_out = dw8250_serial_out38x; + if (of_device_is_compatible(np, "starfive,jh7100-uart")) + p->set_termios = dw8250_do_set_termios; } else if (acpi_dev_present("APMC0D08", NULL, -1)) { p->iotype = UPIO_MEM32; @@ -696,6 +698,7 @@ static const struct of_device_id dw8250_of_match[] = { { .compatible = "cavium,octeon-3860-uart" }, { .compatible = "marvell,armada-38x-uart" }, { .compatible = "renesas,rzn1-uart" }, + { .compatible = "starfive,jh7100-uart" }, { /* Sentinel */ } }; MODULE_DEVICE_TABLE(of, dw8250_of_match); From 20115dbab64b2bf11d9c47840e6958b54ea7d67d Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Sun, 10 Oct 2021 16:48:27 +0200 Subject: [PATCH 15/80] RISC-V: Add initial StarFive JH7100 device tree commit ec85362fb121d0297b9f3bb56816ea6282c34fda upstream. Add initial device tree for the JH7100 RISC-V SoC by StarFive Ltd. This is a test chip for their upcoming JH7110 SoC. The CPU and cache data is based on the device tree in the vendor u-boot port. Acked-by: Palmer Dabbelt Signed-off-by: Emil Renner Berthing --- arch/riscv/boot/dts/starfive/jh7100.dtsi | 230 +++++++++++++++++++++++ 1 file changed, 230 insertions(+) create mode 100644 arch/riscv/boot/dts/starfive/jh7100.dtsi diff --git a/arch/riscv/boot/dts/starfive/jh7100.dtsi b/arch/riscv/boot/dts/starfive/jh7100.dtsi new file mode 100644 index 0000000000000..69f22f9aad9db --- /dev/null +++ b/arch/riscv/boot/dts/starfive/jh7100.dtsi @@ -0,0 +1,230 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * Copyright (C) 2021 StarFive Technology Co., Ltd. + * Copyright (C) 2021 Emil Renner Berthing + */ + +/dts-v1/; +#include +#include + +/ { + compatible = "starfive,jh7100"; + #address-cells = <2>; + #size-cells = <2>; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu@0 { + compatible = "sifive,u74-mc", "riscv"; + reg = <0>; + d-cache-block-size = <64>; + d-cache-sets = <64>; + d-cache-size = <32768>; + d-tlb-sets = <1>; + d-tlb-size = <32>; + device_type = "cpu"; + i-cache-block-size = <64>; + i-cache-sets = <64>; + i-cache-size = <32768>; + i-tlb-sets = <1>; + i-tlb-size = <32>; + mmu-type = "riscv,sv39"; + riscv,isa = "rv64imafdc"; + tlb-split; + + cpu0_intc: interrupt-controller { + compatible = "riscv,cpu-intc"; + interrupt-controller; + #interrupt-cells = <1>; + }; + }; + + cpu@1 { + compatible = "sifive,u74-mc", "riscv"; + reg = <1>; + d-cache-block-size = <64>; + d-cache-sets = <64>; + d-cache-size = <32768>; + d-tlb-sets = <1>; + d-tlb-size = <32>; + device_type = "cpu"; + i-cache-block-size = <64>; + i-cache-sets = <64>; + i-cache-size = <32768>; + i-tlb-sets = <1>; + i-tlb-size = <32>; + mmu-type = "riscv,sv39"; + riscv,isa = "rv64imafdc"; + tlb-split; + + cpu1_intc: interrupt-controller { + compatible = "riscv,cpu-intc"; + interrupt-controller; + #interrupt-cells = <1>; + }; + }; + }; + + osc_sys: osc_sys { + compatible = "fixed-clock"; + #clock-cells = <0>; + /* This value must be overridden by the board */ + clock-frequency = <0>; + }; + + osc_aud: osc_aud { + compatible = "fixed-clock"; + #clock-cells = <0>; + /* This value must be overridden by the board */ + clock-frequency = <0>; + }; + + gmac_rmii_ref: gmac_rmii_ref { + compatible = "fixed-clock"; + #clock-cells = <0>; + /* Should be overridden by the board when needed */ + clock-frequency = <0>; + }; + + gmac_gr_mii_rxclk: gmac_gr_mii_rxclk { + compatible = "fixed-clock"; + #clock-cells = <0>; + /* Should be overridden by the board when needed */ + clock-frequency = <0>; + }; + + soc { + compatible = "simple-bus"; + interrupt-parent = <&plic>; + #address-cells = <2>; + #size-cells = <2>; + ranges; + + clint: clint@2000000 { + compatible = "starfive,jh7100-clint", "sifive,clint0"; + reg = <0x0 0x2000000 0x0 0x10000>; + interrupts-extended = <&cpu0_intc 3 &cpu0_intc 7 + &cpu1_intc 3 &cpu1_intc 7>; + }; + + plic: interrupt-controller@c000000 { + compatible = "starfive,jh7100-plic", "sifive,plic-1.0.0"; + reg = <0x0 0xc000000 0x0 0x4000000>; + interrupts-extended = <&cpu0_intc 11 &cpu0_intc 9 + &cpu1_intc 11 &cpu1_intc 9>; + interrupt-controller; + #address-cells = <0>; + #interrupt-cells = <1>; + riscv,ndev = <127>; + }; + + clkgen: clock-controller@11800000 { + compatible = "starfive,jh7100-clkgen"; + reg = <0x0 0x11800000 0x0 0x10000>; + clocks = <&osc_sys>, <&osc_aud>, <&gmac_rmii_ref>, <&gmac_gr_mii_rxclk>; + clock-names = "osc_sys", "osc_aud", "gmac_rmii_ref", "gmac_gr_mii_rxclk"; + #clock-cells = <1>; + }; + + rstgen: reset-controller@11840000 { + compatible = "starfive,jh7100-reset"; + reg = <0x0 0x11840000 0x0 0x10000>; + #reset-cells = <1>; + }; + + i2c0: i2c@118b0000 { + compatible = "snps,designware-i2c"; + reg = <0x0 0x118b0000 0x0 0x10000>; + clocks = <&clkgen JH7100_CLK_I2C0_CORE>, + <&clkgen JH7100_CLK_I2C0_APB>; + clock-names = "ref", "pclk"; + resets = <&rstgen JH7100_RSTN_I2C0_APB>; + interrupts = <96>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + i2c1: i2c@118c0000 { + compatible = "snps,designware-i2c"; + reg = <0x0 0x118c0000 0x0 0x10000>; + clocks = <&clkgen JH7100_CLK_I2C1_CORE>, + <&clkgen JH7100_CLK_I2C1_APB>; + clock-names = "ref", "pclk"; + resets = <&rstgen JH7100_RSTN_I2C1_APB>; + interrupts = <97>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + gpio: pinctrl@11910000 { + compatible = "starfive,jh7100-pinctrl"; + reg = <0x0 0x11910000 0x0 0x10000>, + <0x0 0x11858000 0x0 0x1000>; + reg-names = "gpio", "padctl"; + clocks = <&clkgen JH7100_CLK_GPIO_APB>; + resets = <&rstgen JH7100_RSTN_GPIO_APB>; + interrupts = <32>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + uart2: serial@12430000 { + compatible = "starfive,jh7100-uart", "snps,dw-apb-uart"; + reg = <0x0 0x12430000 0x0 0x10000>; + clocks = <&clkgen JH7100_CLK_UART2_CORE>, + <&clkgen JH7100_CLK_UART2_APB>; + clock-names = "baudclk", "apb_pclk"; + resets = <&rstgen JH7100_RSTN_UART2_APB>; + interrupts = <72>; + reg-io-width = <4>; + reg-shift = <2>; + status = "disabled"; + }; + + uart3: serial@12440000 { + compatible = "starfive,jh7100-uart", "snps,dw-apb-uart"; + reg = <0x0 0x12440000 0x0 0x10000>; + clocks = <&clkgen JH7100_CLK_UART3_CORE>, + <&clkgen JH7100_CLK_UART3_APB>; + clock-names = "baudclk", "apb_pclk"; + resets = <&rstgen JH7100_RSTN_UART3_APB>; + interrupts = <73>; + reg-io-width = <4>; + reg-shift = <2>; + status = "disabled"; + }; + + i2c2: i2c@12450000 { + compatible = "snps,designware-i2c"; + reg = <0x0 0x12450000 0x0 0x10000>; + clocks = <&clkgen JH7100_CLK_I2C2_CORE>, + <&clkgen JH7100_CLK_I2C2_APB>; + clock-names = "ref", "pclk"; + resets = <&rstgen JH7100_RSTN_I2C2_APB>; + interrupts = <74>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + i2c3: i2c@12460000 { + compatible = "snps,designware-i2c"; + reg = <0x0 0x12460000 0x0 0x10000>; + clocks = <&clkgen JH7100_CLK_I2C3_CORE>, + <&clkgen JH7100_CLK_I2C3_APB>; + clock-names = "ref", "pclk"; + resets = <&rstgen JH7100_RSTN_I2C3_APB>; + interrupts = <75>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + }; +}; From 1b75a47222181ca68c71bd691b20f2dedf22f96e Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Sun, 10 Oct 2021 19:48:36 +0200 Subject: [PATCH 16/80] RISC-V: Add BeagleV Starlight Beta device tree commit a43676272a6e0b398781bc5337ca4cc187ba923d upstream. Add initial device tree for the BeagleV Starlight Beta board. About 300 of these boards were sent out as part of a now cancelled BeagleBoard.org project. I2C timing data is based on the device tree in the vendor u-boot port. Heartbeat LED added by Geert. Acked-by: Palmer Dabbelt Co-developed-by: Geert Uytterhoeven Signed-off-by: Geert Uytterhoeven Signed-off-by: Emil Renner Berthing --- arch/riscv/boot/dts/Makefile | 1 + arch/riscv/boot/dts/starfive/Makefile | 2 + .../dts/starfive/jh7100-beaglev-starlight.dts | 164 ++++++++++++++++++ 3 files changed, 167 insertions(+) create mode 100644 arch/riscv/boot/dts/starfive/Makefile create mode 100644 arch/riscv/boot/dts/starfive/jh7100-beaglev-starlight.dts diff --git a/arch/riscv/boot/dts/Makefile b/arch/riscv/boot/dts/Makefile index fe996b88319eb..ff174996cdfd0 100644 --- a/arch/riscv/boot/dts/Makefile +++ b/arch/riscv/boot/dts/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 subdir-y += sifive +subdir-y += starfive subdir-$(CONFIG_SOC_CANAAN_K210_DTB_BUILTIN) += canaan subdir-y += microchip diff --git a/arch/riscv/boot/dts/starfive/Makefile b/arch/riscv/boot/dts/starfive/Makefile new file mode 100644 index 0000000000000..0ea1bc15ab305 --- /dev/null +++ b/arch/riscv/boot/dts/starfive/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0 +dtb-$(CONFIG_SOC_STARFIVE) += jh7100-beaglev-starlight.dtb diff --git a/arch/riscv/boot/dts/starfive/jh7100-beaglev-starlight.dts b/arch/riscv/boot/dts/starfive/jh7100-beaglev-starlight.dts new file mode 100644 index 0000000000000..c9af67f7a0d20 --- /dev/null +++ b/arch/riscv/boot/dts/starfive/jh7100-beaglev-starlight.dts @@ -0,0 +1,164 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * Copyright (C) 2021 StarFive Technology Co., Ltd. + * Copyright (C) 2021 Emil Renner Berthing + */ + +/dts-v1/; +#include "jh7100.dtsi" +#include +#include +#include + +/ { + model = "BeagleV Starlight Beta"; + compatible = "beagle,beaglev-starlight-jh7100-r0", "starfive,jh7100"; + + aliases { + serial0 = &uart3; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + cpus { + timebase-frequency = <6250000>; + }; + + memory@80000000 { + device_type = "memory"; + reg = <0x0 0x80000000 0x2 0x0>; + }; + + leds { + compatible = "gpio-leds"; + + led-ack { + gpios = <&gpio 43 GPIO_ACTIVE_HIGH>; + color = ; + function = LED_FUNCTION_HEARTBEAT; + linux,default-trigger = "heartbeat"; + label = "ack"; + }; + }; +}; + +&gpio { + i2c0_pins: i2c0-0 { + i2c-pins { + pinmux = , + ; + bias-disable; /* external pull-up */ + input-enable; + input-schmitt-enable; + }; + }; + + i2c1_pins: i2c1-0 { + i2c-pins { + pinmux = , + ; + bias-pull-up; + input-enable; + input-schmitt-enable; + }; + }; + + i2c2_pins: i2c2-0 { + i2c-pins { + pinmux = , + ; + bias-disable; /* external pull-up */ + input-enable; + input-schmitt-enable; + }; + }; + + uart3_pins: uart3-0 { + rx-pins { + pinmux = ; + bias-pull-up; + drive-strength = <14>; + input-enable; + input-schmitt-enable; + slew-rate = <0>; + }; + tx-pins { + pinmux = ; + bias-disable; + drive-strength = <35>; + input-disable; + input-schmitt-disable; + slew-rate = <0>; + }; + }; +}; + +&i2c0 { + clock-frequency = <100000>; + i2c-sda-hold-time-ns = <300>; + i2c-sda-falling-time-ns = <500>; + i2c-scl-falling-time-ns = <500>; + pinctrl-names = "default"; + pinctrl-0 = <&i2c0_pins>; + status = "okay"; + + pmic@5e { + compatible = "ti,tps65086"; + reg = <0x5e>; + gpio-controller; + #gpio-cells = <2>; + + regulators { + }; + }; +}; + +&i2c1 { + clock-frequency = <400000>; + i2c-sda-hold-time-ns = <300>; + i2c-sda-falling-time-ns = <100>; + i2c-scl-falling-time-ns = <100>; + pinctrl-names = "default"; + pinctrl-0 = <&i2c1_pins>; + status = "okay"; +}; + +&i2c2 { + clock-frequency = <100000>; + i2c-sda-hold-time-ns = <300>; + i2c-sda-falling-time-ns = <500>; + i2c-scl-falling-time-ns = <500>; + pinctrl-names = "default"; + pinctrl-0 = <&i2c2_pins>; + status = "okay"; +}; + +&osc_sys { + clock-frequency = <25000000>; +}; + +&osc_aud { + clock-frequency = <27000000>; +}; + +&uart3 { + pinctrl-names = "default"; + pinctrl-0 = <&uart3_pins>; + status = "okay"; +}; From 3e80572441bb4a57c87a38f3601652e55732f89f Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Mon, 20 Dec 2021 13:17:59 +0100 Subject: [PATCH 17/80] reset: starfive-jh7100: Fix 32bit compilation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 299e6f788eab0b0aef97efb29ddc6971e7d0daf3 upstream. We need to include linux/io-64-nonatomic-lo-hi.h or readq/writeq won't be defined when compiling on 32bit architectures: On i386: ../drivers/reset/reset-starfive-jh7100.c: In function ‘jh7100_reset_update’: ../drivers/reset/reset-starfive-jh7100.c:81:10: error: implicit declaration of function ‘readq’; did you mean ‘readl’? [-Werror=implicit-function-declaration] value = readq(reg_assert); ^~~~~ ../drivers/reset/reset-starfive-jh7100.c:86:2: error: implicit declaration of function ‘writeq’; did you mean ‘writel’? [-Werror=implicit-function-declaration] writeq(value, reg_assert); ^~~~~~ On m68k: drivers/reset/reset-starfive-jh7100.c:81:17: error: implicit declaration of function 'readq'; did you mean 'readb'? [-Werror=implicit-function-declaration] drivers/reset/reset-starfive-jh7100.c:86:9: error: implicit declaration of function 'writeq'; did you mean 'writel'? [-Werror=implicit-function-declaration] cc1: all warnings being treated as errors make[3]: *** [scripts/Makefile.build:289: drivers/reset/reset-starfive-jh7100.o] Error 1 make[2]: *** [scripts/Makefile.build:572: drivers/reset] Error 2 make[1]: *** [Makefile:1969: drivers] Error 2 make: *** [Makefile:226: __sub-make] Error 2 Fixes: 0be3a1595bf8 ("reset: starfive-jh7100: Add StarFive JH7100 reset driver") Reported-by: Randy Dunlap Signed-off-by: Emil Renner Berthing Link: https://lore.kernel.org/r/20211220121800.760846-1-kernel@esmil.dk' Signed-off-by: Arnd Bergmann --- drivers/reset/reset-starfive-jh7100.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/reset/reset-starfive-jh7100.c b/drivers/reset/reset-starfive-jh7100.c index e28a19d271cbc..fc44b2fb3e031 100644 --- a/drivers/reset/reset-starfive-jh7100.c +++ b/drivers/reset/reset-starfive-jh7100.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include From 4bfa262a52b73a5eb9165378a6a9fb85f8ddd8a7 Mon Sep 17 00:00:00 2001 From: Xianting Tian Date: Sat, 7 Aug 2021 22:55:37 +0800 Subject: [PATCH 18/80] riscv: add ARCH_DMA_MINALIGN support Introduce ARCH_DMA_MINALIGN to riscv arch. Signed-off-by: Xianting Tian --- arch/riscv/include/asm/cache.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/riscv/include/asm/cache.h b/arch/riscv/include/asm/cache.h index 9b58b104559ee..2945bbe2b6bcb 100644 --- a/arch/riscv/include/asm/cache.h +++ b/arch/riscv/include/asm/cache.h @@ -11,6 +11,8 @@ #define L1_CACHE_BYTES (1 << L1_CACHE_SHIFT) +#define ARCH_DMA_MINALIGN L1_CACHE_BYTES + /* * RISC-V requires the stack pointer to be 16-byte aligned, so ensure that * the flat loader aligns it accordingly. From 7961e2bbd0e46befc8ee6a0c33d4a4d7e4a2b314 Mon Sep 17 00:00:00 2001 From: Matteo Croce Date: Wed, 29 Sep 2021 19:22:32 +0200 Subject: [PATCH 19/80] riscv: optimized memcpy Write a C version of memcpy() which uses the biggest data size allowed, without generating unaligned accesses. The procedure is made of three steps: First copy data one byte at time until the destination buffer is aligned to a long boundary. Then copy the data one long at time shifting the current and the next u8 to compose a long at every cycle. Finally, copy the remainder one byte at time. On a BeagleV, the TCP RX throughput increased by 45%: before: $ iperf3 -c beaglev Connecting to host beaglev, port 5201 [ 5] local 192.168.85.6 port 44840 connected to 192.168.85.48 port 5201 [ ID] Interval Transfer Bitrate Retr Cwnd [ 5] 0.00-1.00 sec 76.4 MBytes 641 Mbits/sec 27 624 KBytes [ 5] 1.00-2.00 sec 72.5 MBytes 608 Mbits/sec 0 708 KBytes [ 5] 2.00-3.00 sec 73.8 MBytes 619 Mbits/sec 10 451 KBytes [ 5] 3.00-4.00 sec 72.5 MBytes 608 Mbits/sec 0 564 KBytes [ 5] 4.00-5.00 sec 73.8 MBytes 619 Mbits/sec 0 658 KBytes [ 5] 5.00-6.00 sec 73.8 MBytes 619 Mbits/sec 14 522 KBytes [ 5] 6.00-7.00 sec 73.8 MBytes 619 Mbits/sec 0 621 KBytes [ 5] 7.00-8.00 sec 72.5 MBytes 608 Mbits/sec 0 706 KBytes [ 5] 8.00-9.00 sec 73.8 MBytes 619 Mbits/sec 20 580 KBytes [ 5] 9.00-10.00 sec 73.8 MBytes 619 Mbits/sec 0 672 KBytes - - - - - - - - - - - - - - - - - - - - - - - - - [ ID] Interval Transfer Bitrate Retr [ 5] 0.00-10.00 sec 736 MBytes 618 Mbits/sec 71 sender [ 5] 0.00-10.01 sec 733 MBytes 615 Mbits/sec receiver after: $ iperf3 -c beaglev Connecting to host beaglev, port 5201 [ 5] local 192.168.85.6 port 44864 connected to 192.168.85.48 port 5201 [ ID] Interval Transfer Bitrate Retr Cwnd [ 5] 0.00-1.00 sec 109 MBytes 912 Mbits/sec 48 559 KBytes [ 5] 1.00-2.00 sec 108 MBytes 902 Mbits/sec 0 690 KBytes [ 5] 2.00-3.00 sec 106 MBytes 891 Mbits/sec 36 396 KBytes [ 5] 3.00-4.00 sec 108 MBytes 902 Mbits/sec 0 567 KBytes [ 5] 4.00-5.00 sec 106 MBytes 891 Mbits/sec 0 699 KBytes [ 5] 5.00-6.00 sec 106 MBytes 891 Mbits/sec 32 414 KBytes [ 5] 6.00-7.00 sec 106 MBytes 891 Mbits/sec 0 583 KBytes [ 5] 7.00-8.00 sec 106 MBytes 891 Mbits/sec 0 708 KBytes [ 5] 8.00-9.00 sec 106 MBytes 891 Mbits/sec 28 433 KBytes [ 5] 9.00-10.00 sec 108 MBytes 902 Mbits/sec 0 591 KBytes - - - - - - - - - - - - - - - - - - - - - - - - - [ ID] Interval Transfer Bitrate Retr [ 5] 0.00-10.00 sec 1.04 GBytes 897 Mbits/sec 144 sender [ 5] 0.00-10.01 sec 1.04 GBytes 894 Mbits/sec receiver And the decreased CPU time of the memcpy() is observable with perf top. This is the `perf top -Ue task-clock` output when doing the test: before: Overhead Shared O Symbol 42.22% [kernel] [k] memcpy 35.00% [kernel] [k] __asm_copy_to_user 3.50% [kernel] [k] sifive_l2_flush64_range 2.30% [kernel] [k] stmmac_napi_poll_rx 1.11% [kernel] [k] memset after: Overhead Shared O Symbol 45.69% [kernel] [k] __asm_copy_to_user 29.06% [kernel] [k] memcpy 4.09% [kernel] [k] sifive_l2_flush64_range 2.77% [kernel] [k] stmmac_napi_poll_rx 1.24% [kernel] [k] memset Signed-off-by: Matteo Croce Reported-by: kernel test robot --- arch/riscv/include/asm/string.h | 8 ++- arch/riscv/kernel/riscv_ksyms.c | 2 - arch/riscv/lib/Makefile | 2 +- arch/riscv/lib/memcpy.S | 108 -------------------------------- arch/riscv/lib/string.c | 90 ++++++++++++++++++++++++++ 5 files changed, 97 insertions(+), 113 deletions(-) delete mode 100644 arch/riscv/lib/memcpy.S create mode 100644 arch/riscv/lib/string.c diff --git a/arch/riscv/include/asm/string.h b/arch/riscv/include/asm/string.h index 9090493665555..6b5d6fc3eab49 100644 --- a/arch/riscv/include/asm/string.h +++ b/arch/riscv/include/asm/string.h @@ -12,9 +12,13 @@ #define __HAVE_ARCH_MEMSET extern asmlinkage void *memset(void *, int, size_t); extern asmlinkage void *__memset(void *, int, size_t); + +#ifdef CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE #define __HAVE_ARCH_MEMCPY -extern asmlinkage void *memcpy(void *, const void *, size_t); -extern asmlinkage void *__memcpy(void *, const void *, size_t); +extern void *memcpy(void *dest, const void *src, size_t count); +extern void *__memcpy(void *dest, const void *src, size_t count); +#endif + #define __HAVE_ARCH_MEMMOVE extern asmlinkage void *memmove(void *, const void *, size_t); extern asmlinkage void *__memmove(void *, const void *, size_t); diff --git a/arch/riscv/kernel/riscv_ksyms.c b/arch/riscv/kernel/riscv_ksyms.c index 5ab1c7e1a6ed5..3f6d512a5b978 100644 --- a/arch/riscv/kernel/riscv_ksyms.c +++ b/arch/riscv/kernel/riscv_ksyms.c @@ -10,8 +10,6 @@ * Assembly functions that may be used (directly or indirectly) by modules */ EXPORT_SYMBOL(memset); -EXPORT_SYMBOL(memcpy); EXPORT_SYMBOL(memmove); EXPORT_SYMBOL(__memset); -EXPORT_SYMBOL(__memcpy); EXPORT_SYMBOL(__memmove); diff --git a/arch/riscv/lib/Makefile b/arch/riscv/lib/Makefile index 25d5c9664e57e..2ffe85d4baeed 100644 --- a/arch/riscv/lib/Makefile +++ b/arch/riscv/lib/Makefile @@ -1,9 +1,9 @@ # SPDX-License-Identifier: GPL-2.0-only lib-y += delay.o -lib-y += memcpy.o lib-y += memset.o lib-y += memmove.o lib-$(CONFIG_MMU) += uaccess.o lib-$(CONFIG_64BIT) += tishift.o +lib-$(CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE) += string.o obj-$(CONFIG_FUNCTION_ERROR_INJECTION) += error-inject.o diff --git a/arch/riscv/lib/memcpy.S b/arch/riscv/lib/memcpy.S deleted file mode 100644 index 51ab716253fa3..0000000000000 --- a/arch/riscv/lib/memcpy.S +++ /dev/null @@ -1,108 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (C) 2013 Regents of the University of California - */ - -#include -#include - -/* void *memcpy(void *, const void *, size_t) */ -ENTRY(__memcpy) -WEAK(memcpy) - move t6, a0 /* Preserve return value */ - - /* Defer to byte-oriented copy for small sizes */ - sltiu a3, a2, 128 - bnez a3, 4f - /* Use word-oriented copy only if low-order bits match */ - andi a3, t6, SZREG-1 - andi a4, a1, SZREG-1 - bne a3, a4, 4f - - beqz a3, 2f /* Skip if already aligned */ - /* - * Round to nearest double word-aligned address - * greater than or equal to start address - */ - andi a3, a1, ~(SZREG-1) - addi a3, a3, SZREG - /* Handle initial misalignment */ - sub a4, a3, a1 -1: - lb a5, 0(a1) - addi a1, a1, 1 - sb a5, 0(t6) - addi t6, t6, 1 - bltu a1, a3, 1b - sub a2, a2, a4 /* Update count */ - -2: - andi a4, a2, ~((16*SZREG)-1) - beqz a4, 4f - add a3, a1, a4 -3: - REG_L a4, 0(a1) - REG_L a5, SZREG(a1) - REG_L a6, 2*SZREG(a1) - REG_L a7, 3*SZREG(a1) - REG_L t0, 4*SZREG(a1) - REG_L t1, 5*SZREG(a1) - REG_L t2, 6*SZREG(a1) - REG_L t3, 7*SZREG(a1) - REG_L t4, 8*SZREG(a1) - REG_L t5, 9*SZREG(a1) - REG_S a4, 0(t6) - REG_S a5, SZREG(t6) - REG_S a6, 2*SZREG(t6) - REG_S a7, 3*SZREG(t6) - REG_S t0, 4*SZREG(t6) - REG_S t1, 5*SZREG(t6) - REG_S t2, 6*SZREG(t6) - REG_S t3, 7*SZREG(t6) - REG_S t4, 8*SZREG(t6) - REG_S t5, 9*SZREG(t6) - REG_L a4, 10*SZREG(a1) - REG_L a5, 11*SZREG(a1) - REG_L a6, 12*SZREG(a1) - REG_L a7, 13*SZREG(a1) - REG_L t0, 14*SZREG(a1) - REG_L t1, 15*SZREG(a1) - addi a1, a1, 16*SZREG - REG_S a4, 10*SZREG(t6) - REG_S a5, 11*SZREG(t6) - REG_S a6, 12*SZREG(t6) - REG_S a7, 13*SZREG(t6) - REG_S t0, 14*SZREG(t6) - REG_S t1, 15*SZREG(t6) - addi t6, t6, 16*SZREG - bltu a1, a3, 3b - andi a2, a2, (16*SZREG)-1 /* Update count */ - -4: - /* Handle trailing misalignment */ - beqz a2, 6f - add a3, a1, a2 - - /* Use word-oriented copy if co-aligned to word boundary */ - or a5, a1, t6 - or a5, a5, a3 - andi a5, a5, 3 - bnez a5, 5f -7: - lw a4, 0(a1) - addi a1, a1, 4 - sw a4, 0(t6) - addi t6, t6, 4 - bltu a1, a3, 7b - - ret - -5: - lb a4, 0(a1) - addi a1, a1, 1 - sb a4, 0(t6) - addi t6, t6, 1 - bltu a1, a3, 5b -6: - ret -END(__memcpy) diff --git a/arch/riscv/lib/string.c b/arch/riscv/lib/string.c new file mode 100644 index 0000000000000..bfc912ee23f8d --- /dev/null +++ b/arch/riscv/lib/string.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * String functions optimized for hardware which doesn't + * handle unaligned memory accesses efficiently. + * + * Copyright (C) 2021 Matteo Croce + */ + +#include +#include + +/* Minimum size for a word copy to be convenient */ +#define BYTES_LONG sizeof(long) +#define WORD_MASK (BYTES_LONG - 1) +#define MIN_THRESHOLD (BYTES_LONG * 2) + +/* convenience union to avoid cast between different pointer types */ +union types { + u8 *as_u8; + unsigned long *as_ulong; + uintptr_t as_uptr; +}; + +union const_types { + const u8 *as_u8; + unsigned long *as_ulong; + uintptr_t as_uptr; +}; + +void *__memcpy(void *dest, const void *src, size_t count) +{ + union const_types s = { .as_u8 = src }; + union types d = { .as_u8 = dest }; + int distance = 0; + + if (!IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)) { + if (count < MIN_THRESHOLD) + goto copy_remainder; + + /* Copy a byte at time until destination is aligned. */ + for (; d.as_uptr & WORD_MASK; count--) + *d.as_u8++ = *s.as_u8++; + + distance = s.as_uptr & WORD_MASK; + } + + if (distance) { + unsigned long last, next; + + /* + * s is distance bytes ahead of d, and d just reached + * the alignment boundary. Move s backward to word align it + * and shift data to compensate for distance, in order to do + * word-by-word copy. + */ + s.as_u8 -= distance; + + next = s.as_ulong[0]; + for (; count >= BYTES_LONG; count -= BYTES_LONG) { + last = next; + next = s.as_ulong[1]; + + d.as_ulong[0] = last >> (distance * 8) | + next << ((BYTES_LONG - distance) * 8); + + d.as_ulong++; + s.as_ulong++; + } + + /* Restore s with the original offset. */ + s.as_u8 += distance; + } else { + /* + * If the source and dest lower bits are the same, do a simple + * 32/64 bit wide copy. + */ + for (; count >= BYTES_LONG; count -= BYTES_LONG) + *d.as_ulong++ = *s.as_ulong++; + } + +copy_remainder: + while (count--) + *d.as_u8++ = *s.as_u8++; + + return dest; +} +EXPORT_SYMBOL(__memcpy); + +void *memcpy(void *dest, const void *src, size_t count) __weak __alias(__memcpy); +EXPORT_SYMBOL(memcpy); From 2506d868997e3288f81ebba840bfc276273fafd5 Mon Sep 17 00:00:00 2001 From: Matteo Croce Date: Wed, 29 Sep 2021 19:22:33 +0200 Subject: [PATCH 20/80] riscv: optimized memmove When the destination buffer is before the source one, or when the buffers doesn't overlap, it's safe to use memcpy() instead, which is optimized to use a bigger data size possible. Signed-off-by: Matteo Croce Reported-by: kernel test robot --- arch/riscv/include/asm/string.h | 6 ++-- arch/riscv/kernel/riscv_ksyms.c | 2 -- arch/riscv/lib/Makefile | 1 - arch/riscv/lib/memmove.S | 64 --------------------------------- arch/riscv/lib/string.c | 23 ++++++++++++ 5 files changed, 26 insertions(+), 70 deletions(-) delete mode 100644 arch/riscv/lib/memmove.S diff --git a/arch/riscv/include/asm/string.h b/arch/riscv/include/asm/string.h index 6b5d6fc3eab49..25d9b9078569c 100644 --- a/arch/riscv/include/asm/string.h +++ b/arch/riscv/include/asm/string.h @@ -17,11 +17,11 @@ extern asmlinkage void *__memset(void *, int, size_t); #define __HAVE_ARCH_MEMCPY extern void *memcpy(void *dest, const void *src, size_t count); extern void *__memcpy(void *dest, const void *src, size_t count); +#define __HAVE_ARCH_MEMMOVE +extern void *memmove(void *dest, const void *src, size_t count); +extern void *__memmove(void *dest, const void *src, size_t count); #endif -#define __HAVE_ARCH_MEMMOVE -extern asmlinkage void *memmove(void *, const void *, size_t); -extern asmlinkage void *__memmove(void *, const void *, size_t); /* For those files which don't want to check by kasan. */ #if defined(CONFIG_KASAN) && !defined(__SANITIZE_ADDRESS__) #define memcpy(dst, src, len) __memcpy(dst, src, len) diff --git a/arch/riscv/kernel/riscv_ksyms.c b/arch/riscv/kernel/riscv_ksyms.c index 3f6d512a5b978..361565c4db7e0 100644 --- a/arch/riscv/kernel/riscv_ksyms.c +++ b/arch/riscv/kernel/riscv_ksyms.c @@ -10,6 +10,4 @@ * Assembly functions that may be used (directly or indirectly) by modules */ EXPORT_SYMBOL(memset); -EXPORT_SYMBOL(memmove); EXPORT_SYMBOL(__memset); -EXPORT_SYMBOL(__memmove); diff --git a/arch/riscv/lib/Makefile b/arch/riscv/lib/Makefile index 2ffe85d4baeed..484f5ff7b508b 100644 --- a/arch/riscv/lib/Makefile +++ b/arch/riscv/lib/Makefile @@ -1,7 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only lib-y += delay.o lib-y += memset.o -lib-y += memmove.o lib-$(CONFIG_MMU) += uaccess.o lib-$(CONFIG_64BIT) += tishift.o lib-$(CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE) += string.o diff --git a/arch/riscv/lib/memmove.S b/arch/riscv/lib/memmove.S deleted file mode 100644 index 07d1d2152ba5c..0000000000000 --- a/arch/riscv/lib/memmove.S +++ /dev/null @@ -1,64 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ - -#include -#include - -ENTRY(__memmove) -WEAK(memmove) - move t0, a0 - move t1, a1 - - beq a0, a1, exit_memcpy - beqz a2, exit_memcpy - srli t2, a2, 0x2 - - slt t3, a0, a1 - beqz t3, do_reverse - - andi a2, a2, 0x3 - li t4, 1 - beqz t2, byte_copy - -word_copy: - lw t3, 0(a1) - addi t2, t2, -1 - addi a1, a1, 4 - sw t3, 0(a0) - addi a0, a0, 4 - bnez t2, word_copy - beqz a2, exit_memcpy - j byte_copy - -do_reverse: - add a0, a0, a2 - add a1, a1, a2 - andi a2, a2, 0x3 - li t4, -1 - beqz t2, reverse_byte_copy - -reverse_word_copy: - addi a1, a1, -4 - addi t2, t2, -1 - lw t3, 0(a1) - addi a0, a0, -4 - sw t3, 0(a0) - bnez t2, reverse_word_copy - beqz a2, exit_memcpy - -reverse_byte_copy: - addi a0, a0, -1 - addi a1, a1, -1 - -byte_copy: - lb t3, 0(a1) - addi a2, a2, -1 - sb t3, 0(a0) - add a1, a1, t4 - add a0, a0, t4 - bnez a2, byte_copy - -exit_memcpy: - move a0, t0 - move a1, t1 - ret -END(__memmove) diff --git a/arch/riscv/lib/string.c b/arch/riscv/lib/string.c index bfc912ee23f8d..4442ca5ef13c8 100644 --- a/arch/riscv/lib/string.c +++ b/arch/riscv/lib/string.c @@ -88,3 +88,26 @@ EXPORT_SYMBOL(__memcpy); void *memcpy(void *dest, const void *src, size_t count) __weak __alias(__memcpy); EXPORT_SYMBOL(memcpy); + +/* + * Simply check if the buffer overlaps an call memcpy() in case, + * otherwise do a simple one byte at time backward copy. + */ +void *__memmove(void *dest, const void *src, size_t count) +{ + if (dest < src || src + count <= dest) + return __memcpy(dest, src, count); + + if (dest > src) { + const char *s = src + count; + char *tmp = dest + count; + + while (count--) + *--tmp = *--s; + } + return dest; +} +EXPORT_SYMBOL(__memmove); + +void *memmove(void *dest, const void *src, size_t count) __weak __alias(__memmove); +EXPORT_SYMBOL(memmove); From b08d7511cd7d86e62c93add1ed7b444abf808125 Mon Sep 17 00:00:00 2001 From: Matteo Croce Date: Wed, 29 Sep 2021 19:22:34 +0200 Subject: [PATCH 21/80] riscv: optimized memset The generic memset is defined as a byte at time write. This is always safe, but it's slower than a 4 byte or even 8 byte write. Write a generic memset which fills the data one byte at time until the destination is aligned, then fills using the largest size allowed, and finally fills the remaining data one byte at time. Signed-off-by: Matteo Croce --- arch/riscv/include/asm/string.h | 10 +-- arch/riscv/kernel/Makefile | 1 - arch/riscv/kernel/riscv_ksyms.c | 13 ---- arch/riscv/lib/Makefile | 1 - arch/riscv/lib/memset.S | 113 -------------------------------- arch/riscv/lib/string.c | 41 ++++++++++++ 6 files changed, 44 insertions(+), 135 deletions(-) delete mode 100644 arch/riscv/kernel/riscv_ksyms.c delete mode 100644 arch/riscv/lib/memset.S diff --git a/arch/riscv/include/asm/string.h b/arch/riscv/include/asm/string.h index 25d9b9078569c..90500635035a3 100644 --- a/arch/riscv/include/asm/string.h +++ b/arch/riscv/include/asm/string.h @@ -6,14 +6,10 @@ #ifndef _ASM_RISCV_STRING_H #define _ASM_RISCV_STRING_H -#include -#include - -#define __HAVE_ARCH_MEMSET -extern asmlinkage void *memset(void *, int, size_t); -extern asmlinkage void *__memset(void *, int, size_t); - #ifdef CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE +#define __HAVE_ARCH_MEMSET +extern void *memset(void *s, int c, size_t count); +extern void *__memset(void *s, int c, size_t count); #define __HAVE_ARCH_MEMCPY extern void *memcpy(void *dest, const void *src, size_t count); extern void *__memcpy(void *dest, const void *src, size_t count); diff --git a/arch/riscv/kernel/Makefile b/arch/riscv/kernel/Makefile index 3397ddac1a30c..fecf038224359 100644 --- a/arch/riscv/kernel/Makefile +++ b/arch/riscv/kernel/Makefile @@ -31,7 +31,6 @@ obj-y += syscall_table.o obj-y += sys_riscv.o obj-y += time.o obj-y += traps.o -obj-y += riscv_ksyms.o obj-y += stacktrace.o obj-y += cacheinfo.o obj-y += patch.o diff --git a/arch/riscv/kernel/riscv_ksyms.c b/arch/riscv/kernel/riscv_ksyms.c deleted file mode 100644 index 361565c4db7e0..0000000000000 --- a/arch/riscv/kernel/riscv_ksyms.c +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) 2017 Zihao Yu - */ - -#include -#include - -/* - * Assembly functions that may be used (directly or indirectly) by modules - */ -EXPORT_SYMBOL(memset); -EXPORT_SYMBOL(__memset); diff --git a/arch/riscv/lib/Makefile b/arch/riscv/lib/Makefile index 484f5ff7b508b..e33263cc622a1 100644 --- a/arch/riscv/lib/Makefile +++ b/arch/riscv/lib/Makefile @@ -1,6 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only lib-y += delay.o -lib-y += memset.o lib-$(CONFIG_MMU) += uaccess.o lib-$(CONFIG_64BIT) += tishift.o lib-$(CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE) += string.o diff --git a/arch/riscv/lib/memset.S b/arch/riscv/lib/memset.S deleted file mode 100644 index 34c5360c6705c..0000000000000 --- a/arch/riscv/lib/memset.S +++ /dev/null @@ -1,113 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (C) 2013 Regents of the University of California - */ - - -#include -#include - -/* void *memset(void *, int, size_t) */ -ENTRY(__memset) -WEAK(memset) - move t0, a0 /* Preserve return value */ - - /* Defer to byte-oriented fill for small sizes */ - sltiu a3, a2, 16 - bnez a3, 4f - - /* - * Round to nearest XLEN-aligned address - * greater than or equal to start address - */ - addi a3, t0, SZREG-1 - andi a3, a3, ~(SZREG-1) - beq a3, t0, 2f /* Skip if already aligned */ - /* Handle initial misalignment */ - sub a4, a3, t0 -1: - sb a1, 0(t0) - addi t0, t0, 1 - bltu t0, a3, 1b - sub a2, a2, a4 /* Update count */ - -2: /* Duff's device with 32 XLEN stores per iteration */ - /* Broadcast value into all bytes */ - andi a1, a1, 0xff - slli a3, a1, 8 - or a1, a3, a1 - slli a3, a1, 16 - or a1, a3, a1 -#ifdef CONFIG_64BIT - slli a3, a1, 32 - or a1, a3, a1 -#endif - - /* Calculate end address */ - andi a4, a2, ~(SZREG-1) - add a3, t0, a4 - - andi a4, a4, 31*SZREG /* Calculate remainder */ - beqz a4, 3f /* Shortcut if no remainder */ - neg a4, a4 - addi a4, a4, 32*SZREG /* Calculate initial offset */ - - /* Adjust start address with offset */ - sub t0, t0, a4 - - /* Jump into loop body */ - /* Assumes 32-bit instruction lengths */ - la a5, 3f -#ifdef CONFIG_64BIT - srli a4, a4, 1 -#endif - add a5, a5, a4 - jr a5 -3: - REG_S a1, 0(t0) - REG_S a1, SZREG(t0) - REG_S a1, 2*SZREG(t0) - REG_S a1, 3*SZREG(t0) - REG_S a1, 4*SZREG(t0) - REG_S a1, 5*SZREG(t0) - REG_S a1, 6*SZREG(t0) - REG_S a1, 7*SZREG(t0) - REG_S a1, 8*SZREG(t0) - REG_S a1, 9*SZREG(t0) - REG_S a1, 10*SZREG(t0) - REG_S a1, 11*SZREG(t0) - REG_S a1, 12*SZREG(t0) - REG_S a1, 13*SZREG(t0) - REG_S a1, 14*SZREG(t0) - REG_S a1, 15*SZREG(t0) - REG_S a1, 16*SZREG(t0) - REG_S a1, 17*SZREG(t0) - REG_S a1, 18*SZREG(t0) - REG_S a1, 19*SZREG(t0) - REG_S a1, 20*SZREG(t0) - REG_S a1, 21*SZREG(t0) - REG_S a1, 22*SZREG(t0) - REG_S a1, 23*SZREG(t0) - REG_S a1, 24*SZREG(t0) - REG_S a1, 25*SZREG(t0) - REG_S a1, 26*SZREG(t0) - REG_S a1, 27*SZREG(t0) - REG_S a1, 28*SZREG(t0) - REG_S a1, 29*SZREG(t0) - REG_S a1, 30*SZREG(t0) - REG_S a1, 31*SZREG(t0) - addi t0, t0, 32*SZREG - bltu t0, a3, 3b - andi a2, a2, SZREG-1 /* Update count */ - -4: - /* Handle trailing misalignment */ - beqz a2, 6f - add a3, t0, a2 -5: - sb a1, 0(t0) - addi t0, t0, 1 - bltu t0, a3, 5b -6: - ret -END(__memset) diff --git a/arch/riscv/lib/string.c b/arch/riscv/lib/string.c index 4442ca5ef13c8..871ffbfac0ab4 100644 --- a/arch/riscv/lib/string.c +++ b/arch/riscv/lib/string.c @@ -111,3 +111,44 @@ EXPORT_SYMBOL(__memmove); void *memmove(void *dest, const void *src, size_t count) __weak __alias(__memmove); EXPORT_SYMBOL(memmove); + +void *__memset(void *s, int c, size_t count) +{ + union types dest = { .as_u8 = s }; + + if (count >= MIN_THRESHOLD) { + unsigned long cu = (unsigned long)c; + + /* Compose an ulong with 'c' repeated 4/8 times */ +#ifdef CONFIG_ARCH_HAS_FAST_MULTIPLIER + cu *= 0x0101010101010101UL; +#else + cu |= cu << 8; + cu |= cu << 16; + /* Suppress warning on 32 bit machines */ + cu |= (cu << 16) << 16; +#endif + if (!IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)) { + /* + * Fill the buffer one byte at time until + * the destination is word aligned. + */ + for (; count && dest.as_uptr & WORD_MASK; count--) + *dest.as_u8++ = c; + } + + /* Copy using the largest size allowed */ + for (; count >= BYTES_LONG; count -= BYTES_LONG) + *dest.as_ulong++ = cu; + } + + /* copy the remainder */ + while (count--) + *dest.as_u8++ = c; + + return s; +} +EXPORT_SYMBOL(__memset); + +void *memset(void *s, int c, size_t count) __weak __alias(__memset); +EXPORT_SYMBOL(memset); From 19d5ffc72cde81db8ee6d83dbde19d3e250979eb Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Mon, 6 Dec 2021 21:27:07 +0100 Subject: [PATCH 22/80] riscv: Add -ffreestanding for string functions The string library implements memset, memcpy and other library functions, so tell the compiler not to optimise such code to just calls to themselves. This is correct for all compilers, but for some reason only Clang builds break without this flag. Signed-off-by: Emil Renner Berthing --- arch/riscv/lib/Makefile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/riscv/lib/Makefile b/arch/riscv/lib/Makefile index e33263cc622a1..6dfa919d4cd67 100644 --- a/arch/riscv/lib/Makefile +++ b/arch/riscv/lib/Makefile @@ -4,4 +4,9 @@ lib-$(CONFIG_MMU) += uaccess.o lib-$(CONFIG_64BIT) += tishift.o lib-$(CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE) += string.o +# string.o implements standard library functions like memset/memcpy etc. +# Use -ffreestanding to ensure that the compiler does not try to "optimize" +# them into calls to themselves. +CFLAGS_string.o := -ffreestanding + obj-$(CONFIG_FUNCTION_ERROR_INJECTION) += error-inject.o From 367e473cf4a2da1cea7d0f82820b9afd5bcd1fdf Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Fri, 3 Dec 2021 00:36:16 +0100 Subject: [PATCH 23/80] clk: starfive: jh7100: Don't round divisor up twice The problem is best illustrated by an example. Suppose a consumer wants a 4MHz clock rate from a divider with a 10MHz parent. It would then call clk_round_rate(clk, 4000000) which would call into our determine_rate() callback that correctly rounds up and finds that a divisor of 3 gives the highest possible frequency below the requested 4MHz and returns 10000000 / 3 = 3333333Hz. However the consumer would then call clk_set_rate(clk, 3333333) but since 3333333 doesn't divide 10000000 evenly our set_rate() callback would again round the divisor up and set it to 4 which results in an unnecessarily low rate of 2.5MHz. Fix it by using DIV_ROUND_CLOSEST in the set_rate() callback. Signed-off-by: Emil Renner Berthing --- drivers/clk/starfive/clk-starfive-jh7100.c | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/drivers/clk/starfive/clk-starfive-jh7100.c b/drivers/clk/starfive/clk-starfive-jh7100.c index 25d31afa0f871..db6a4dc203aff 100644 --- a/drivers/clk/starfive/clk-starfive-jh7100.c +++ b/drivers/clk/starfive/clk-starfive-jh7100.c @@ -399,22 +399,13 @@ static unsigned long jh7100_clk_recalc_rate(struct clk_hw *hw, return div ? parent_rate / div : 0; } -static unsigned long jh7100_clk_bestdiv(struct jh7100_clk *clk, - unsigned long rate, unsigned long parent) -{ - unsigned long max = clk->max_div; - unsigned long div = DIV_ROUND_UP(parent, rate); - - return min(div, max); -} - static int jh7100_clk_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) { struct jh7100_clk *clk = jh7100_clk_from(hw); unsigned long parent = req->best_parent_rate; unsigned long rate = clamp(req->rate, req->min_rate, req->max_rate); - unsigned long div = jh7100_clk_bestdiv(clk, rate, parent); + unsigned long div = min_t(unsigned long, DIV_ROUND_UP(parent, rate), clk->max_div); unsigned long result = parent / div; /* @@ -442,7 +433,8 @@ static int jh7100_clk_set_rate(struct clk_hw *hw, unsigned long parent_rate) { struct jh7100_clk *clk = jh7100_clk_from(hw); - unsigned long div = jh7100_clk_bestdiv(clk, rate, parent_rate); + unsigned long div = clamp(DIV_ROUND_CLOSEST(parent_rate, rate), + 1UL, (unsigned long)clk->max_div); jh7100_clk_reg_rmw(clk, JH7100_CLK_DIV_MASK, div); return 0; From f1fe4d266662188074223f9f77cd1d64857da31b Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Sat, 4 Dec 2021 16:37:03 +0100 Subject: [PATCH 24/80] clk: starfive: jh7100: Handle audio_div clock properly It turns out the audio_div clock is a fractional divider where the lowest byte of the ctrl register is the integer part of the divider and the 2nd byte is the number of 100th added to the divider. The children of this clock is used by the audio peripherals for their sample rate clock, so round to the closest possible rate rather than always rounding down like regular dividers. Signed-off-by: Emil Renner Berthing --- drivers/clk/starfive/clk-starfive-jh7100.c | 68 +++++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) diff --git a/drivers/clk/starfive/clk-starfive-jh7100.c b/drivers/clk/starfive/clk-starfive-jh7100.c index db6a4dc203aff..4b59338b5d7d4 100644 --- a/drivers/clk/starfive/clk-starfive-jh7100.c +++ b/drivers/clk/starfive/clk-starfive-jh7100.c @@ -32,6 +32,13 @@ #define JH7100_CLK_MUX_MASK GENMASK(27, 24) #define JH7100_CLK_MUX_SHIFT 24 #define JH7100_CLK_DIV_MASK GENMASK(23, 0) +#define JH7100_CLK_FRAC_MASK GENMASK(15, 8) +#define JH7100_CLK_FRAC_SHIFT 8 +#define JH7100_CLK_INT_MASK GENMASK(7, 0) + +/* fractional divider min/max */ +#define JH7100_CLK_FRAC_MIN 100UL +#define JH7100_CLK_FRAC_MAX 25599UL /* clock data */ #define JH7100_GATE(_idx, _name, _flags, _parent) [_idx] = { \ @@ -55,6 +62,13 @@ .parents = { [0] = _parent }, \ } +#define JH7100_FDIV(_idx, _name, _parent) [_idx] = { \ + .name = _name, \ + .flags = 0, \ + .max = JH7100_CLK_FRAC_MAX, \ + .parents = { [0] = _parent }, \ +} + #define JH7100__MUX(_idx, _name, _nparents, ...) [_idx] = { \ .name = _name, \ .flags = 0, \ @@ -225,7 +239,7 @@ static const struct { JH7100__MUX(JH7100_CLK_USBPHY_25M, "usbphy_25m", 2, JH7100_CLK_OSC_SYS, JH7100_CLK_USBPHY_PLLDIV25M), - JH7100__DIV(JH7100_CLK_AUDIO_DIV, "audio_div", 131072, JH7100_CLK_AUDIO_ROOT), + JH7100_FDIV(JH7100_CLK_AUDIO_DIV, "audio_div", JH7100_CLK_AUDIO_ROOT), JH7100_GATE(JH7100_CLK_AUDIO_SRC, "audio_src", 0, JH7100_CLK_AUDIO_DIV), JH7100_GATE(JH7100_CLK_AUDIO_12288, "audio_12288", 0, JH7100_CLK_OSC_AUD), JH7100_GDIV(JH7100_CLK_VIN_SRC, "vin_src", 0, 4, JH7100_CLK_VIN_ROOT), @@ -440,6 +454,49 @@ static int jh7100_clk_set_rate(struct clk_hw *hw, return 0; } +static unsigned long jh7100_clk_frac_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct jh7100_clk *clk = jh7100_clk_from(hw); + u32 reg = jh7100_clk_reg_get(clk); + unsigned long div100 = 100 * (reg & JH7100_CLK_INT_MASK) + + ((reg & JH7100_CLK_FRAC_MASK) >> JH7100_CLK_FRAC_SHIFT); + + return (div100 >= JH7100_CLK_FRAC_MIN) ? 100 * parent_rate / div100 : 0; +} + +static int jh7100_clk_frac_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + unsigned long parent100 = 100 * req->best_parent_rate; + unsigned long rate = clamp(req->rate, req->min_rate, req->max_rate); + unsigned long div100 = clamp(DIV_ROUND_CLOSEST(parent100, rate), + JH7100_CLK_FRAC_MIN, JH7100_CLK_FRAC_MAX); + unsigned long result = parent100 / div100; + + /* clamp the result as in jh7100_clk_determine_rate() above */ + if (result > req->max_rate && div100 < JH7100_CLK_FRAC_MAX) + result = parent100 / (div100 + 1); + if (result < req->min_rate && div100 > JH7100_CLK_FRAC_MIN) + result = parent100 / (div100 - 1); + + req->rate = result; + return 0; +} + +static int jh7100_clk_frac_set_rate(struct clk_hw *hw, + unsigned long rate, + unsigned long parent_rate) +{ + struct jh7100_clk *clk = jh7100_clk_from(hw); + unsigned long div100 = clamp(DIV_ROUND_CLOSEST(100 * parent_rate, rate), + JH7100_CLK_FRAC_MIN, JH7100_CLK_FRAC_MAX); + u32 value = ((div100 % 100) << JH7100_CLK_FRAC_SHIFT) | (div100 / 100); + + jh7100_clk_reg_rmw(clk, JH7100_CLK_DIV_MASK, value); + return 0; +} + static u8 jh7100_clk_get_parent(struct clk_hw *hw) { struct jh7100_clk *clk = jh7100_clk_from(hw); @@ -526,6 +583,13 @@ static const struct clk_ops jh7100_clk_div_ops = { .debug_init = jh7100_clk_debug_init, }; +static const struct clk_ops jh7100_clk_fdiv_ops = { + .recalc_rate = jh7100_clk_frac_recalc_rate, + .determine_rate = jh7100_clk_frac_determine_rate, + .set_rate = jh7100_clk_frac_set_rate, + .debug_init = jh7100_clk_debug_init, +}; + static const struct clk_ops jh7100_clk_gdiv_ops = { .enable = jh7100_clk_enable, .disable = jh7100_clk_disable, @@ -564,6 +628,8 @@ static const struct clk_ops *__init jh7100_clk_ops(u32 max) if (max & JH7100_CLK_DIV_MASK) { if (max & JH7100_CLK_ENABLE) return &jh7100_clk_gdiv_ops; + if (max == JH7100_CLK_FRAC_MAX) + return &jh7100_clk_fdiv_ops; return &jh7100_clk_div_ops; } From 97d86a80c3b939f026573b39bc33fd02a6096abc Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 25 Nov 2021 14:21:18 +0100 Subject: [PATCH 25/80] riscv: dts: starfive: Group tuples in interrupt properties To improve human readability and enable automatic validation, the tuples in the various properties containing interrupt specifiers should be grouped. Fix this by grouping the tuples of "interrupts-extended" properties using angle brackets. Signed-off-by: Geert Uytterhoeven --- arch/riscv/boot/dts/starfive/jh7100.dtsi | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/riscv/boot/dts/starfive/jh7100.dtsi b/arch/riscv/boot/dts/starfive/jh7100.dtsi index 69f22f9aad9db..d74fc29af6428 100644 --- a/arch/riscv/boot/dts/starfive/jh7100.dtsi +++ b/arch/riscv/boot/dts/starfive/jh7100.dtsi @@ -106,15 +106,15 @@ clint: clint@2000000 { compatible = "starfive,jh7100-clint", "sifive,clint0"; reg = <0x0 0x2000000 0x0 0x10000>; - interrupts-extended = <&cpu0_intc 3 &cpu0_intc 7 - &cpu1_intc 3 &cpu1_intc 7>; + interrupts-extended = <&cpu0_intc 3>, <&cpu0_intc 7>, + <&cpu1_intc 3>, <&cpu1_intc 7>; }; plic: interrupt-controller@c000000 { compatible = "starfive,jh7100-plic", "sifive,plic-1.0.0"; reg = <0x0 0xc000000 0x0 0x4000000>; - interrupts-extended = <&cpu0_intc 11 &cpu0_intc 9 - &cpu1_intc 11 &cpu1_intc 9>; + interrupts-extended = <&cpu0_intc 11>, <&cpu0_intc 9>, + <&cpu1_intc 11>, <&cpu1_intc 9>; interrupt-controller; #address-cells = <0>; #interrupt-cells = <1>; From c936e32b513589f148c59bb1f0bdfc58c8b25c8b Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Wed, 24 Nov 2021 01:33:43 +0100 Subject: [PATCH 26/80] dt-bindings: clock: Add JH7100 audio clock definitions Add all clock outputs for the StarFive JH7100 audio clock generator. Signed-off-by: Emil Renner Berthing --- .../dt-bindings/clock/starfive-jh7100-audio.h | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 include/dt-bindings/clock/starfive-jh7100-audio.h diff --git a/include/dt-bindings/clock/starfive-jh7100-audio.h b/include/dt-bindings/clock/starfive-jh7100-audio.h new file mode 100644 index 0000000000000..fbb4eae6572bf --- /dev/null +++ b/include/dt-bindings/clock/starfive-jh7100-audio.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/* + * Copyright (C) 2021 Emil Renner Berthing + */ + +#ifndef __DT_BINDINGS_CLOCK_STARFIVE_JH7100_AUDIO_H__ +#define __DT_BINDINGS_CLOCK_STARFIVE_JH7100_AUDIO_H__ + +#define JH7100_AUDCLK_ADC_MCLK 0 +#define JH7100_AUDCLK_I2S1_MCLK 1 +#define JH7100_AUDCLK_I2SADC_APB 2 +#define JH7100_AUDCLK_I2SADC_BCLK 3 +#define JH7100_AUDCLK_I2SADC_BCLK_N 4 +#define JH7100_AUDCLK_I2SADC_LRCLK 5 +#define JH7100_AUDCLK_PDM_APB 6 +#define JH7100_AUDCLK_PDM_MCLK 7 +#define JH7100_AUDCLK_I2SVAD_APB 8 +#define JH7100_AUDCLK_SPDIF 9 +#define JH7100_AUDCLK_SPDIF_APB 10 +#define JH7100_AUDCLK_PWMDAC_APB 11 +#define JH7100_AUDCLK_DAC_MCLK 12 +#define JH7100_AUDCLK_I2SDAC_APB 13 +#define JH7100_AUDCLK_I2SDAC_BCLK 14 +#define JH7100_AUDCLK_I2SDAC_BCLK_N 15 +#define JH7100_AUDCLK_I2SDAC_LRCLK 16 +#define JH7100_AUDCLK_I2S1_APB 17 +#define JH7100_AUDCLK_I2S1_BCLK 18 +#define JH7100_AUDCLK_I2S1_BCLK_N 19 +#define JH7100_AUDCLK_I2S1_LRCLK 20 +#define JH7100_AUDCLK_I2SDAC16K_APB 21 +#define JH7100_AUDCLK_APB0_BUS 22 +#define JH7100_AUDCLK_DMA1P_AHB 23 +#define JH7100_AUDCLK_USB_APB 24 +#define JH7100_AUDCLK_USB_LPM 25 +#define JH7100_AUDCLK_USB_STB 26 +#define JH7100_AUDCLK_APB_EN 27 +#define JH7100_AUDCLK_VAD_MEM 28 + +#define JH7100_AUDCLK_END 29 + +#endif /* __DT_BINDINGS_CLOCK_STARFIVE_JH7100_AUDIO_H__ */ From 1aa3aa6a9aa6dfd2d572b744e9d924f382853e9b Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Tue, 7 Dec 2021 21:48:38 +0100 Subject: [PATCH 27/80] dt-bindings: clock: Add starfive,jh7100-audclk bindings Add bindings for the audio clocks on the StarFive JH7100 RISC-V SoC. Signed-off-by: Emil Renner Berthing --- .../clock/starfive,jh7100-audclk.yaml | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/starfive,jh7100-audclk.yaml diff --git a/Documentation/devicetree/bindings/clock/starfive,jh7100-audclk.yaml b/Documentation/devicetree/bindings/clock/starfive,jh7100-audclk.yaml new file mode 100644 index 0000000000000..3e7bd23903b34 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/starfive,jh7100-audclk.yaml @@ -0,0 +1,57 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/starfive,jh7100-audclk.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive JH7100 Audio Clock Generator + +maintainers: + - Emil Renner Berthing + +properties: + compatible: + const: starfive,jh7100-audclk + + reg: + maxItems: 1 + + clocks: + items: + - description: Audio source clock + - description: External 12.228MHz clock + - description: Domain 7 AHB bus clock + + clock-names: + items: + - const: audio_src + - const: audio_12288 + - const: dom7ahb_bus + + '#clock-cells': + const: 1 + description: + See for valid indices. + +required: + - compatible + - reg + - clocks + - clock-names + - '#clock-cells' + +additionalProperties: false + +examples: + - | + #include + + clock-controller@10480000 { + compatible = "starfive,jh7100-audclk"; + reg = <0x10480000 0x10000>; + clocks = <&clkgen JH7100_CLK_AUDIO_SRC>, + <&clkgen JH7100_CLK_AUDIO_12288>, + <&clkgen JH7100_CLK_DOM7AHB_BUS>; + clock-names = "audio_src", "audio_12288", "dom7ahb_bus"; + #clock-cells = <1>; + }; From 43cfd06eb01bd43dc1102db13dc05bcbcf51fc31 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Sat, 20 Nov 2021 17:11:59 +0100 Subject: [PATCH 28/80] clk: starfive: jh7100: Make hw clock implementation reusable The JH7100 has additional audio and video clocks at different memory ranges, but they use the same register layout. Add a header and export the starfive_jh7100_clk_ops function so the clock implementation can be reused by drivers handling these clocks. Signed-off-by: Emil Renner Berthing --- drivers/clk/starfive/clk-starfive-jh7100.c | 96 ++----------------- drivers/clk/starfive/clk-starfive-jh7100.h | 103 +++++++++++++++++++++ 2 files changed, 110 insertions(+), 89 deletions(-) create mode 100644 drivers/clk/starfive/clk-starfive-jh7100.h diff --git a/drivers/clk/starfive/clk-starfive-jh7100.c b/drivers/clk/starfive/clk-starfive-jh7100.c index 4b59338b5d7d4..a6708f9ebf4c5 100644 --- a/drivers/clk/starfive/clk-starfive-jh7100.c +++ b/drivers/clk/starfive/clk-starfive-jh7100.c @@ -20,83 +20,15 @@ #include +#include "clk-starfive-jh7100.h" + /* external clocks */ #define JH7100_CLK_OSC_SYS (JH7100_CLK_END + 0) #define JH7100_CLK_OSC_AUD (JH7100_CLK_END + 1) #define JH7100_CLK_GMAC_RMII_REF (JH7100_CLK_END + 2) #define JH7100_CLK_GMAC_GR_MII_RX (JH7100_CLK_END + 3) -/* register fields */ -#define JH7100_CLK_ENABLE BIT(31) -#define JH7100_CLK_INVERT BIT(30) -#define JH7100_CLK_MUX_MASK GENMASK(27, 24) -#define JH7100_CLK_MUX_SHIFT 24 -#define JH7100_CLK_DIV_MASK GENMASK(23, 0) -#define JH7100_CLK_FRAC_MASK GENMASK(15, 8) -#define JH7100_CLK_FRAC_SHIFT 8 -#define JH7100_CLK_INT_MASK GENMASK(7, 0) - -/* fractional divider min/max */ -#define JH7100_CLK_FRAC_MIN 100UL -#define JH7100_CLK_FRAC_MAX 25599UL - -/* clock data */ -#define JH7100_GATE(_idx, _name, _flags, _parent) [_idx] = { \ - .name = _name, \ - .flags = CLK_SET_RATE_PARENT | (_flags), \ - .max = JH7100_CLK_ENABLE, \ - .parents = { [0] = _parent }, \ -} - -#define JH7100__DIV(_idx, _name, _max, _parent) [_idx] = { \ - .name = _name, \ - .flags = 0, \ - .max = _max, \ - .parents = { [0] = _parent }, \ -} - -#define JH7100_GDIV(_idx, _name, _flags, _max, _parent) [_idx] = { \ - .name = _name, \ - .flags = _flags, \ - .max = JH7100_CLK_ENABLE | (_max), \ - .parents = { [0] = _parent }, \ -} - -#define JH7100_FDIV(_idx, _name, _parent) [_idx] = { \ - .name = _name, \ - .flags = 0, \ - .max = JH7100_CLK_FRAC_MAX, \ - .parents = { [0] = _parent }, \ -} - -#define JH7100__MUX(_idx, _name, _nparents, ...) [_idx] = { \ - .name = _name, \ - .flags = 0, \ - .max = ((_nparents) - 1) << JH7100_CLK_MUX_SHIFT, \ - .parents = { __VA_ARGS__ }, \ -} - -#define JH7100_GMUX(_idx, _name, _flags, _nparents, ...) [_idx] = { \ - .name = _name, \ - .flags = _flags, \ - .max = JH7100_CLK_ENABLE | \ - (((_nparents) - 1) << JH7100_CLK_MUX_SHIFT), \ - .parents = { __VA_ARGS__ }, \ -} - -#define JH7100__INV(_idx, _name, _parent) [_idx] = { \ - .name = _name, \ - .flags = CLK_SET_RATE_PARENT, \ - .max = JH7100_CLK_INVERT, \ - .parents = { [0] = _parent }, \ -} - -static const struct { - const char *name; - unsigned long flags; - u32 max; - u8 parents[4]; -} jh7100_clk_data[] __initconst = { +static const struct jh7100_clk_data jh7100_clk_data[] __initconst = { JH7100__MUX(JH7100_CLK_CPUNDBUS_ROOT, "cpundbus_root", 4, JH7100_CLK_OSC_SYS, JH7100_CLK_PLL0_OUT, @@ -337,21 +269,6 @@ static const struct { JH7100_GATE(JH7100_CLK_SYSERR_APB, "syserr_apb", 0, JH7100_CLK_APB2_BUS), }; -struct jh7100_clk { - struct clk_hw hw; - unsigned int idx; - unsigned int max_div; -}; - -struct jh7100_clk_priv { - /* protect clk enable and set rate/parent from happening at the same time */ - spinlock_t rmw_lock; - struct device *dev; - void __iomem *base; - struct clk_hw *pll[3]; - struct jh7100_clk reg[JH7100_CLK_PLL0_OUT]; -}; - static struct jh7100_clk *jh7100_clk_from(struct clk_hw *hw) { return container_of(hw, struct jh7100_clk, hw); @@ -623,7 +540,7 @@ static const struct clk_ops jh7100_clk_inv_ops = { .debug_init = jh7100_clk_debug_init, }; -static const struct clk_ops *__init jh7100_clk_ops(u32 max) +const struct clk_ops *starfive_jh7100_clk_ops(u32 max) { if (max & JH7100_CLK_DIV_MASK) { if (max & JH7100_CLK_ENABLE) @@ -644,6 +561,7 @@ static const struct clk_ops *__init jh7100_clk_ops(u32 max) return &jh7100_clk_inv_ops; } +EXPORT_SYMBOL_GPL(starfive_jh7100_clk_ops); static struct clk_hw *jh7100_clk_get(struct of_phandle_args *clkspec, void *data) { @@ -665,7 +583,7 @@ static int __init clk_starfive_jh7100_probe(struct platform_device *pdev) unsigned int idx; int ret; - priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + priv = devm_kzalloc(&pdev->dev, struct_size(priv, reg, JH7100_CLK_PLL0_OUT), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -695,7 +613,7 @@ static int __init clk_starfive_jh7100_probe(struct platform_device *pdev) struct clk_parent_data parents[4] = {}; struct clk_init_data init = { .name = jh7100_clk_data[idx].name, - .ops = jh7100_clk_ops(max), + .ops = starfive_jh7100_clk_ops(max), .parent_data = parents, .num_parents = ((max & JH7100_CLK_MUX_MASK) >> JH7100_CLK_MUX_SHIFT) + 1, .flags = jh7100_clk_data[idx].flags, diff --git a/drivers/clk/starfive/clk-starfive-jh7100.h b/drivers/clk/starfive/clk-starfive-jh7100.h new file mode 100644 index 0000000000000..f7e638a5b0038 --- /dev/null +++ b/drivers/clk/starfive/clk-starfive-jh7100.h @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * StarFive JH7100 Clock Generator Driver + * + * Copyright (C) 2021 Emil Renner Berthing + */ + +#ifndef _CLK_STARFIVE_JH7100_H_ +#define _CLK_STARFIVE_JH7100_H_ + +#include +#include + +/* register fields */ +#define JH7100_CLK_ENABLE BIT(31) +#define JH7100_CLK_INVERT BIT(30) +#define JH7100_CLK_MUX_MASK GENMASK(27, 24) +#define JH7100_CLK_MUX_SHIFT 24 +#define JH7100_CLK_DIV_MASK GENMASK(23, 0) +#define JH7100_CLK_FRAC_MASK GENMASK(15, 8) +#define JH7100_CLK_FRAC_SHIFT 8 +#define JH7100_CLK_INT_MASK GENMASK(7, 0) + +/* fractional divider min/max */ +#define JH7100_CLK_FRAC_MIN 100UL +#define JH7100_CLK_FRAC_MAX 25599UL + +/* clock data */ +struct jh7100_clk_data { + const char *name; + unsigned long flags; + u32 max; + u8 parents[4]; +}; + +#define JH7100_GATE(_idx, _name, _flags, _parent) [_idx] = { \ + .name = _name, \ + .flags = CLK_SET_RATE_PARENT | (_flags), \ + .max = JH7100_CLK_ENABLE, \ + .parents = { [0] = _parent }, \ +} + +#define JH7100__DIV(_idx, _name, _max, _parent) [_idx] = { \ + .name = _name, \ + .flags = 0, \ + .max = _max, \ + .parents = { [0] = _parent }, \ +} + +#define JH7100_GDIV(_idx, _name, _flags, _max, _parent) [_idx] = { \ + .name = _name, \ + .flags = _flags, \ + .max = JH7100_CLK_ENABLE | (_max), \ + .parents = { [0] = _parent }, \ +} + +#define JH7100_FDIV(_idx, _name, _parent) [_idx] = { \ + .name = _name, \ + .flags = 0, \ + .max = JH7100_CLK_FRAC_MAX, \ + .parents = { [0] = _parent }, \ +} + +#define JH7100__MUX(_idx, _name, _nparents, ...) [_idx] = { \ + .name = _name, \ + .flags = 0, \ + .max = ((_nparents) - 1) << JH7100_CLK_MUX_SHIFT, \ + .parents = { __VA_ARGS__ }, \ +} + +#define JH7100_GMUX(_idx, _name, _flags, _nparents, ...) [_idx] = { \ + .name = _name, \ + .flags = _flags, \ + .max = JH7100_CLK_ENABLE | \ + (((_nparents) - 1) << JH7100_CLK_MUX_SHIFT), \ + .parents = { __VA_ARGS__ }, \ +} + +#define JH7100__INV(_idx, _name, _parent) [_idx] = { \ + .name = _name, \ + .flags = CLK_SET_RATE_PARENT, \ + .max = JH7100_CLK_INVERT, \ + .parents = { [0] = _parent }, \ +} + +struct jh7100_clk { + struct clk_hw hw; + unsigned int idx; + unsigned int max_div; +}; + +struct jh7100_clk_priv { + /* protect clk enable and set rate/parent from happening at the same time */ + spinlock_t rmw_lock; + struct device *dev; + void __iomem *base; + struct clk_hw *pll[3]; + struct jh7100_clk reg[]; +}; + +const struct clk_ops *starfive_jh7100_clk_ops(u32 max); + +#endif From 72d458c5d49dc921e771091f5f5d72a2f9914527 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Sat, 20 Nov 2021 17:12:49 +0100 Subject: [PATCH 29/80] clk: starfive: jh7100: Support more clock types Unlike the system clocks there are audio clocks that combine both multiplexer/divider and gate/multiplexer/divider, so add support for that. Signed-off-by: Emil Renner Berthing --- drivers/clk/starfive/clk-starfive-jh7100.c | 26 ++++++++++++++++++++++ drivers/clk/starfive/clk-starfive-jh7100.h | 15 +++++++++++++ 2 files changed, 41 insertions(+) diff --git a/drivers/clk/starfive/clk-starfive-jh7100.c b/drivers/clk/starfive/clk-starfive-jh7100.c index a6708f9ebf4c5..691aeebc70927 100644 --- a/drivers/clk/starfive/clk-starfive-jh7100.c +++ b/drivers/clk/starfive/clk-starfive-jh7100.c @@ -534,6 +534,27 @@ static const struct clk_ops jh7100_clk_gmux_ops = { .debug_init = jh7100_clk_debug_init, }; +static const struct clk_ops jh7100_clk_mdiv_ops = { + .recalc_rate = jh7100_clk_recalc_rate, + .determine_rate = jh7100_clk_determine_rate, + .get_parent = jh7100_clk_get_parent, + .set_parent = jh7100_clk_set_parent, + .set_rate = jh7100_clk_set_rate, + .debug_init = jh7100_clk_debug_init, +}; + +static const struct clk_ops jh7100_clk_gmd_ops = { + .enable = jh7100_clk_enable, + .disable = jh7100_clk_disable, + .is_enabled = jh7100_clk_is_enabled, + .recalc_rate = jh7100_clk_recalc_rate, + .determine_rate = jh7100_clk_determine_rate, + .get_parent = jh7100_clk_get_parent, + .set_parent = jh7100_clk_set_parent, + .set_rate = jh7100_clk_set_rate, + .debug_init = jh7100_clk_debug_init, +}; + static const struct clk_ops jh7100_clk_inv_ops = { .get_phase = jh7100_clk_get_phase, .set_phase = jh7100_clk_set_phase, @@ -543,6 +564,11 @@ static const struct clk_ops jh7100_clk_inv_ops = { const struct clk_ops *starfive_jh7100_clk_ops(u32 max) { if (max & JH7100_CLK_DIV_MASK) { + if (max & JH7100_CLK_MUX_MASK) { + if (max & JH7100_CLK_ENABLE) + return &jh7100_clk_gmd_ops; + return &jh7100_clk_mdiv_ops; + } if (max & JH7100_CLK_ENABLE) return &jh7100_clk_gdiv_ops; if (max == JH7100_CLK_FRAC_MAX) diff --git a/drivers/clk/starfive/clk-starfive-jh7100.h b/drivers/clk/starfive/clk-starfive-jh7100.h index f7e638a5b0038..5c9812c58392f 100644 --- a/drivers/clk/starfive/clk-starfive-jh7100.h +++ b/drivers/clk/starfive/clk-starfive-jh7100.h @@ -76,6 +76,21 @@ struct jh7100_clk_data { .parents = { __VA_ARGS__ }, \ } +#define JH7100_MDIV(_idx, _name, _max, _nparents, ...) [_idx] = { \ + .name = _name, \ + .flags = 0, \ + .max = (((_nparents) - 1) << JH7100_CLK_MUX_SHIFT) | (_max), \ + .parents = { __VA_ARGS__ }, \ +} + +#define JH7100__GMD(_idx, _name, _flags, _max, _nparents, ...) [_idx] = { \ + .name = _name, \ + .flags = _flags, \ + .max = JH7100_CLK_ENABLE | \ + (((_nparents) - 1) << JH7100_CLK_MUX_SHIFT) | (_max), \ + .parents = { __VA_ARGS__ }, \ +} + #define JH7100__INV(_idx, _name, _parent) [_idx] = { \ .name = _name, \ .flags = CLK_SET_RATE_PARENT, \ From 476b25dc221855848ee23e9eb66209e797cf229c Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Wed, 24 Nov 2021 01:34:35 +0100 Subject: [PATCH 30/80] clk: starfive: Add JH7100 audio clock driver Add a driver for the audio clocks on the Starfive JH7100 RISC-V SoC. Signed-off-by: Emil Renner Berthing --- MAINTAINERS | 8 +- drivers/clk/starfive/Kconfig | 7 + drivers/clk/starfive/Makefile | 1 + .../clk/starfive/clk-starfive-jh7100-audio.c | 170 ++++++++++++++++++ 4 files changed, 182 insertions(+), 4 deletions(-) create mode 100644 drivers/clk/starfive/clk-starfive-jh7100-audio.c diff --git a/MAINTAINERS b/MAINTAINERS index 02ade8c318719..7cfcafed65759 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -18156,12 +18156,12 @@ M: Ion Badulescu S: Odd Fixes F: drivers/net/ethernet/adaptec/starfire* -STARFIVE JH7100 CLOCK DRIVER +STARFIVE JH7100 CLOCK DRIVERS M: Emil Renner Berthing S: Maintained -F: Documentation/devicetree/bindings/clock/starfive,jh7100-clkgen.yaml -F: drivers/clk/starfive/clk-starfive-jh7100.c -F: include/dt-bindings/clock/starfive-jh7100.h +F: Documentation/devicetree/bindings/clock/starfive,jh7100-*.yaml +F: drivers/clk/starfive/clk-starfive-jh7100* +F: include/dt-bindings/clock/starfive-jh7100*.h STARFIVE JH7100 PINCTRL DRIVER M: Emil Renner Berthing diff --git a/drivers/clk/starfive/Kconfig b/drivers/clk/starfive/Kconfig index c0fa9d5e641fe..532b6957742bc 100644 --- a/drivers/clk/starfive/Kconfig +++ b/drivers/clk/starfive/Kconfig @@ -7,3 +7,10 @@ config CLK_STARFIVE_JH7100 help Say yes here to support the clock controller on the StarFive JH7100 SoC. + +config CLK_STARFIVE_JH7100_AUDIO + tristate "StarFive JH7100 audio clock support" + depends on CLK_STARFIVE_JH7100 + default SOC_STARFIVE + help + Say yes here to support the audio clocks on the StarFive JH7100 SoC. diff --git a/drivers/clk/starfive/Makefile b/drivers/clk/starfive/Makefile index 09759cc735307..0fa8ecb9ec1c6 100644 --- a/drivers/clk/starfive/Makefile +++ b/drivers/clk/starfive/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 # StarFive Clock obj-$(CONFIG_CLK_STARFIVE_JH7100) += clk-starfive-jh7100.o +obj-$(CONFIG_CLK_STARFIVE_JH7100_AUDIO) += clk-starfive-jh7100-audio.o diff --git a/drivers/clk/starfive/clk-starfive-jh7100-audio.c b/drivers/clk/starfive/clk-starfive-jh7100-audio.c new file mode 100644 index 0000000000000..e5a59bccda60f --- /dev/null +++ b/drivers/clk/starfive/clk-starfive-jh7100-audio.c @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * StarFive JH7100 Audio Clock Driver + * + * Copyright (C) 2021 Emil Renner Berthing + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "clk-starfive-jh7100.h" + +/* external clocks */ +#define JH7100_AUDCLK_AUDIO_SRC (JH7100_AUDCLK_END + 0) +#define JH7100_AUDCLK_AUDIO_12288 (JH7100_AUDCLK_END + 1) +#define JH7100_AUDCLK_DOM7AHB_BUS (JH7100_AUDCLK_END + 2) +#define JH7100_AUDCLK_I2SADC_BCLK_IOPAD (JH7100_AUDCLK_END + 3) +#define JH7100_AUDCLK_I2SADC_LRCLK_IOPAD (JH7100_AUDCLK_END + 4) +#define JH7100_AUDCLK_I2SDAC_BCLK_IOPAD (JH7100_AUDCLK_END + 5) +#define JH7100_AUDCLK_I2SDAC_LRCLK_IOPAD (JH7100_AUDCLK_END + 6) +#define JH7100_AUDCLK_VAD_INTMEM (JH7100_AUDCLK_END + 7) + +static const struct jh7100_clk_data jh7100_audclk_data[] = { + JH7100__GMD(JH7100_AUDCLK_ADC_MCLK, "adc_mclk", 0, 15, 2, + JH7100_AUDCLK_AUDIO_SRC, + JH7100_AUDCLK_AUDIO_12288), + JH7100__GMD(JH7100_AUDCLK_I2S1_MCLK, "i2s1_mclk", 0, 15, 2, + JH7100_AUDCLK_AUDIO_SRC, + JH7100_AUDCLK_AUDIO_12288), + JH7100_GATE(JH7100_AUDCLK_I2SADC_APB, "i2sadc_apb", 0, JH7100_AUDCLK_APB0_BUS), + JH7100_MDIV(JH7100_AUDCLK_I2SADC_BCLK, "i2sadc_bclk", 31, 2, + JH7100_AUDCLK_ADC_MCLK, + JH7100_AUDCLK_I2SADC_BCLK_IOPAD), + JH7100__INV(JH7100_AUDCLK_I2SADC_BCLK_N, "i2sadc_bclk_n", JH7100_AUDCLK_I2SADC_BCLK), + JH7100_MDIV(JH7100_AUDCLK_I2SADC_LRCLK, "i2sadc_lrclk", 63, 3, + JH7100_AUDCLK_I2SADC_BCLK_N, + JH7100_AUDCLK_I2SADC_LRCLK_IOPAD, + JH7100_AUDCLK_I2SADC_BCLK), + JH7100_GATE(JH7100_AUDCLK_PDM_APB, "pdm_apb", 0, JH7100_AUDCLK_APB0_BUS), + JH7100__GMD(JH7100_AUDCLK_PDM_MCLK, "pdm_mclk", 0, 15, 2, + JH7100_AUDCLK_AUDIO_SRC, + JH7100_AUDCLK_AUDIO_12288), + JH7100_GATE(JH7100_AUDCLK_I2SVAD_APB, "i2svad_apb", 0, JH7100_AUDCLK_APB0_BUS), + JH7100__GMD(JH7100_AUDCLK_SPDIF, "spdif", 0, 15, 2, + JH7100_AUDCLK_AUDIO_SRC, + JH7100_AUDCLK_AUDIO_12288), + JH7100_GATE(JH7100_AUDCLK_SPDIF_APB, "spdif_apb", 0, JH7100_AUDCLK_APB0_BUS), + JH7100_GATE(JH7100_AUDCLK_PWMDAC_APB, "pwmdac_apb", 0, JH7100_AUDCLK_APB0_BUS), + JH7100__GMD(JH7100_AUDCLK_DAC_MCLK, "dac_mclk", 0, 15, 2, + JH7100_AUDCLK_AUDIO_SRC, + JH7100_AUDCLK_AUDIO_12288), + JH7100_GATE(JH7100_AUDCLK_I2SDAC_APB, "i2sdac_apb", 0, JH7100_AUDCLK_APB0_BUS), + JH7100_MDIV(JH7100_AUDCLK_I2SDAC_BCLK, "i2sdac_bclk", 31, 2, + JH7100_AUDCLK_DAC_MCLK, + JH7100_AUDCLK_I2SDAC_BCLK_IOPAD), + JH7100__INV(JH7100_AUDCLK_I2SDAC_BCLK_N, "i2sdac_bclk_n", JH7100_AUDCLK_I2SDAC_BCLK), + JH7100_MDIV(JH7100_AUDCLK_I2SDAC_LRCLK, "i2sdac_lrclk", 31, 2, + JH7100_AUDCLK_I2S1_MCLK, + JH7100_AUDCLK_I2SDAC_BCLK_IOPAD), + JH7100_GATE(JH7100_AUDCLK_I2S1_APB, "i2s1_apb", 0, JH7100_AUDCLK_APB0_BUS), + JH7100_MDIV(JH7100_AUDCLK_I2S1_BCLK, "i2s1_bclk", 31, 2, + JH7100_AUDCLK_I2S1_MCLK, + JH7100_AUDCLK_I2SDAC_BCLK_IOPAD), + JH7100__INV(JH7100_AUDCLK_I2S1_BCLK_N, "i2s1_bclk_n", JH7100_AUDCLK_I2S1_BCLK), + JH7100_MDIV(JH7100_AUDCLK_I2S1_LRCLK, "i2s1_lrclk", 63, 3, + JH7100_AUDCLK_I2S1_BCLK_N, + JH7100_AUDCLK_I2SDAC_LRCLK_IOPAD), + JH7100_GATE(JH7100_AUDCLK_I2SDAC16K_APB, "i2s1dac16k_apb", 0, JH7100_AUDCLK_APB0_BUS), + JH7100__DIV(JH7100_AUDCLK_APB0_BUS, "apb0_bus", 8, JH7100_AUDCLK_DOM7AHB_BUS), + JH7100_GATE(JH7100_AUDCLK_DMA1P_AHB, "dma1p_ahb", 0, JH7100_AUDCLK_DOM7AHB_BUS), + JH7100_GATE(JH7100_AUDCLK_USB_APB, "usb_apb", CLK_IGNORE_UNUSED, JH7100_AUDCLK_APB_EN), + JH7100_GDIV(JH7100_AUDCLK_USB_LPM, "usb_lpm", CLK_IGNORE_UNUSED, 4, JH7100_AUDCLK_USB_APB), + JH7100_GDIV(JH7100_AUDCLK_USB_STB, "usb_stb", CLK_IGNORE_UNUSED, 3, JH7100_AUDCLK_USB_APB), + JH7100__DIV(JH7100_AUDCLK_APB_EN, "apb_en", 8, JH7100_AUDCLK_DOM7AHB_BUS), + JH7100__MUX(JH7100_AUDCLK_VAD_MEM, "vad_mem", 2, + JH7100_AUDCLK_VAD_INTMEM, + JH7100_AUDCLK_AUDIO_12288), +}; + +static struct clk_hw *jh7100_audclk_get(struct of_phandle_args *clkspec, void *data) +{ + struct jh7100_clk_priv *priv = data; + unsigned int idx = clkspec->args[0]; + + if (idx < JH7100_AUDCLK_END) + return &priv->reg[idx].hw; + + return ERR_PTR(-EINVAL); +} + +static int jh7100_audclk_probe(struct platform_device *pdev) +{ + struct jh7100_clk_priv *priv; + unsigned int idx; + int ret; + + priv = devm_kzalloc(&pdev->dev, struct_size(priv, reg, JH7100_AUDCLK_END), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + spin_lock_init(&priv->rmw_lock); + priv->dev = &pdev->dev; + priv->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + for (idx = 0; idx < JH7100_AUDCLK_END; idx++) { + u32 max = jh7100_audclk_data[idx].max; + struct clk_parent_data parents[4] = {}; + struct clk_init_data init = { + .name = jh7100_audclk_data[idx].name, + .ops = starfive_jh7100_clk_ops(max), + .parent_data = parents, + .num_parents = ((max & JH7100_CLK_MUX_MASK) >> JH7100_CLK_MUX_SHIFT) + 1, + .flags = jh7100_audclk_data[idx].flags, + }; + struct jh7100_clk *clk = &priv->reg[idx]; + unsigned int i; + + for (i = 0; i < init.num_parents; i++) { + unsigned int pidx = jh7100_audclk_data[idx].parents[i]; + + if (pidx < JH7100_AUDCLK_END) + parents[i].hw = &priv->reg[pidx].hw; + else if (pidx == JH7100_AUDCLK_AUDIO_SRC) + parents[i].fw_name = "audio_src"; + else if (pidx == JH7100_AUDCLK_AUDIO_12288) + parents[i].fw_name = "audio_12288"; + else if (pidx == JH7100_AUDCLK_DOM7AHB_BUS) + parents[i].fw_name = "dom7ahb_bus"; + } + + clk->hw.init = &init; + clk->idx = idx; + clk->max_div = max & JH7100_CLK_DIV_MASK; + + ret = devm_clk_hw_register(priv->dev, &clk->hw); + if (ret) + return ret; + } + + return devm_of_clk_add_hw_provider(priv->dev, jh7100_audclk_get, priv); +} + +static const struct of_device_id jh7100_audclk_match[] = { + { .compatible = "starfive,jh7100-audclk" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, jh7100_audclk_match); + +static struct platform_driver jh7100_audclk_driver = { + .probe = jh7100_audclk_probe, + .driver = { + .name = "clk-starfive-jh7100-audio", + .of_match_table = jh7100_audclk_match, + }, +}; +module_platform_driver(jh7100_audclk_driver); + +MODULE_AUTHOR("Emil Renner Berthing"); +MODULE_DESCRIPTION("StarFive JH7100 audio clock driver"); +MODULE_LICENSE("GPL"); From 27f13a492d0aa677a0b542f89e5c3132f4be5348 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Sat, 20 Nov 2021 17:13:22 +0100 Subject: [PATCH 31/80] RISC-V: Add StarFive JH7100 audio clock node Add device tree node for the audio clocks on the StarFive JH7100 RISC-V SoC. Signed-off-by: Emil Renner Berthing --- arch/riscv/boot/dts/starfive/jh7100.dtsi | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/arch/riscv/boot/dts/starfive/jh7100.dtsi b/arch/riscv/boot/dts/starfive/jh7100.dtsi index d74fc29af6428..31b4ccc282e0b 100644 --- a/arch/riscv/boot/dts/starfive/jh7100.dtsi +++ b/arch/riscv/boot/dts/starfive/jh7100.dtsi @@ -121,6 +121,16 @@ riscv,ndev = <127>; }; + audclk: clock-controller@10480000 { + compatible = "starfive,jh7100-audclk"; + reg = <0x0 0x10480000 0x0 0x10000>; + clocks = <&clkgen JH7100_CLK_AUDIO_SRC>, + <&clkgen JH7100_CLK_AUDIO_12288>, + <&clkgen JH7100_CLK_DOM7AHB_BUS>; + clock-names = "audio_src", "audio_12288", "dom7ahb_bus"; + #clock-cells = <1>; + }; + clkgen: clock-controller@11800000 { compatible = "starfive,jh7100-clkgen"; reg = <0x0 0x11800000 0x0 0x10000>; From 10d23f93264c58c3b93209d70cd25897b7ac8ca6 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Sat, 20 Nov 2021 19:29:25 +0100 Subject: [PATCH 32/80] dt-bindings: reset: Add StarFive JH7100 audio reset definitions Add all resets for the StarFive JH7100 audio reset controller. Signed-off-by: Emil Renner Berthing --- .../dt-bindings/reset/starfive-jh7100-audio.h | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 include/dt-bindings/reset/starfive-jh7100-audio.h diff --git a/include/dt-bindings/reset/starfive-jh7100-audio.h b/include/dt-bindings/reset/starfive-jh7100-audio.h new file mode 100644 index 0000000000000..30e3d4cf067a6 --- /dev/null +++ b/include/dt-bindings/reset/starfive-jh7100-audio.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/* + * Copyright (C) 2021 Emil Renner Berthing + */ + +#ifndef __DT_BINDINGS_RESET_STARFIVE_JH7100_AUDIO_H__ +#define __DT_BINDINGS_RESET_STARFIVE_JH7100_AUDIO_H__ + +#define JH7100_AUDRSTN_APB_BUS 0 +#define JH7100_AUDRSTN_I2SADC_APB 1 +#define JH7100_AUDRSTN_I2SADC_SRST 2 +#define JH7100_AUDRSTN_PDM_APB 3 +#define JH7100_AUDRSTN_I2SVAD_APB 4 +#define JH7100_AUDRSTN_I2SVAD_SRST 5 +#define JH7100_AUDRSTN_SPDIF_APB 6 +#define JH7100_AUDRSTN_PWMDAC_APB 7 +#define JH7100_AUDRSTN_I2SDAC_APB 8 +#define JH7100_AUDRSTN_I2SDAC_SRST 9 +#define JH7100_AUDRSTN_I2S1_APB 10 +#define JH7100_AUDRSTN_I2S1_SRST 11 +#define JH7100_AUDRSTN_I2SDAC16K_APB 12 +#define JH7100_AUDRSTN_I2SDAC16K_SRST 13 +#define JH7100_AUDRSTN_DMA1P_AHB 14 +#define JH7100_AUDRSTN_USB_APB 15 +#define JH7100_AUDRST_USB_AXI 16 +#define JH7100_AUDRST_USB_PWRUP_RST_N 17 +#define JH7100_AUDRST_USB_PONRST 18 + +#define JH7100_AUDRSTN_END 19 + +#endif /* __DT_BINDINGS_RESET_STARFIVE_JH7100_AUDIO_H__ */ From 30416e5bf1734085a862bb0b0d8f118f237ef5fb Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Tue, 7 Dec 2021 21:48:51 +0100 Subject: [PATCH 33/80] dt-bindings: reset: Add starfive,jh7100-audrst bindings Add bindings for the audio reset controller on the StarFive JH7100 RISC-V SoC. Signed-off-by: Emil Renner Berthing --- .../reset/starfive,jh7100-audrst.yaml | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 Documentation/devicetree/bindings/reset/starfive,jh7100-audrst.yaml diff --git a/Documentation/devicetree/bindings/reset/starfive,jh7100-audrst.yaml b/Documentation/devicetree/bindings/reset/starfive,jh7100-audrst.yaml new file mode 100644 index 0000000000000..6ed85cc84c955 --- /dev/null +++ b/Documentation/devicetree/bindings/reset/starfive,jh7100-audrst.yaml @@ -0,0 +1,38 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/reset/starfive,jh7100-audrst.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive JH7100 SoC Audio Reset Controller Device Tree Bindings + +maintainers: + - Emil Renner Berthing + +properties: + compatible: + enum: + - starfive,jh7100-audrst + + reg: + maxItems: 1 + + "#reset-cells": + const: 1 + +required: + - compatible + - reg + - "#reset-cells" + +additionalProperties: false + +examples: + - | + reset-controller@10490000 { + compatible = "starfive,jh7100-audrst"; + reg = <0x10490000 0x10000>; + #reset-cells = <1>; + }; + +... From 09ed909380bdf713aca856c54367266900529ba1 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Sat, 20 Nov 2021 18:30:33 +0100 Subject: [PATCH 34/80] reset: Create subdirectory for StarFive drivers This moves the StarFive JH7100 reset driver to a new subdirectory in preparation for adding more StarFive reset drivers. Signed-off-by: Emil Renner Berthing --- MAINTAINERS | 2 +- drivers/reset/Kconfig | 8 +------- drivers/reset/Makefile | 2 +- drivers/reset/starfive/Kconfig | 8 ++++++++ drivers/reset/starfive/Makefile | 2 ++ drivers/reset/{ => starfive}/reset-starfive-jh7100.c | 0 6 files changed, 13 insertions(+), 9 deletions(-) create mode 100644 drivers/reset/starfive/Kconfig create mode 100644 drivers/reset/starfive/Makefile rename drivers/reset/{ => starfive}/reset-starfive-jh7100.c (100%) diff --git a/MAINTAINERS b/MAINTAINERS index 7cfcafed65759..ada639d6a0106 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -18175,7 +18175,7 @@ STARFIVE JH7100 RESET CONTROLLER DRIVER M: Emil Renner Berthing S: Maintained F: Documentation/devicetree/bindings/reset/starfive,jh7100-reset.yaml -F: drivers/reset/reset-starfive-jh7100.c +F: drivers/reset/starfive/reset-starfive-jh7100.c F: include/dt-bindings/reset/starfive-jh7100.h STATIC BRANCH/CALL diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index 6f8ba0ddc05fe..6f6295a968627 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -224,13 +224,6 @@ config RESET_SOCFPGA This enables the reset driver for the SoCFPGA ARMv7 platforms. This driver gets initialized early during platform init calls. -config RESET_STARFIVE_JH7100 - bool "StarFive JH7100 Reset Driver" - depends on SOC_STARFIVE || COMPILE_TEST - default SOC_STARFIVE - help - This enables the reset controller driver for the StarFive JH7100 SoC. - config RESET_SUNXI bool "Allwinner SoCs Reset Driver" if COMPILE_TEST && !ARCH_SUNXI default ARCH_SUNXI @@ -282,6 +275,7 @@ config RESET_ZYNQ help This enables the reset controller driver for Xilinx Zynq SoCs. +source "drivers/reset/starfive/Kconfig" source "drivers/reset/sti/Kconfig" source "drivers/reset/hisilicon/Kconfig" source "drivers/reset/tegra/Kconfig" diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index bd0a97be18b50..e7edddcd82d5d 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 obj-y += core.o obj-y += hisilicon/ +obj-y += starfive/ obj-$(CONFIG_ARCH_STI) += sti/ obj-$(CONFIG_ARCH_TEGRA) += tegra/ obj-$(CONFIG_RESET_A10SR) += reset-a10sr.o @@ -29,7 +30,6 @@ obj-$(CONFIG_RESET_RZG2L_USBPHY_CTRL) += reset-rzg2l-usbphy-ctrl.o obj-$(CONFIG_RESET_SCMI) += reset-scmi.o obj-$(CONFIG_RESET_SIMPLE) += reset-simple.o obj-$(CONFIG_RESET_SOCFPGA) += reset-socfpga.o -obj-$(CONFIG_RESET_STARFIVE_JH7100) += reset-starfive-jh7100.o obj-$(CONFIG_RESET_SUNXI) += reset-sunxi.o obj-$(CONFIG_RESET_TI_SCI) += reset-ti-sci.o obj-$(CONFIG_RESET_TI_SYSCON) += reset-ti-syscon.o diff --git a/drivers/reset/starfive/Kconfig b/drivers/reset/starfive/Kconfig new file mode 100644 index 0000000000000..cddebdba71773 --- /dev/null +++ b/drivers/reset/starfive/Kconfig @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config RESET_STARFIVE_JH7100 + bool "StarFive JH7100 Reset Driver" + depends on SOC_STARFIVE || COMPILE_TEST + default SOC_STARFIVE + help + This enables the reset controller driver for the StarFive JH7100 SoC. diff --git a/drivers/reset/starfive/Makefile b/drivers/reset/starfive/Makefile new file mode 100644 index 0000000000000..670d049423f54 --- /dev/null +++ b/drivers/reset/starfive/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_RESET_STARFIVE_JH7100) += reset-starfive-jh7100.o diff --git a/drivers/reset/reset-starfive-jh7100.c b/drivers/reset/starfive/reset-starfive-jh7100.c similarity index 100% rename from drivers/reset/reset-starfive-jh7100.c rename to drivers/reset/starfive/reset-starfive-jh7100.c From b796dcdd9f5a180c15cd8c979ade2411255fbd53 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Wed, 24 Nov 2021 01:30:54 +0100 Subject: [PATCH 35/80] reset: starfive: Use 32bit I/O on 32bit registers The driver currently uses 64bit I/O on the 32bit registers. This works because there are 4 assert registers and 4 status register, so they're only ever accessed on 64bit boundaries. There are however other reset controllers for audio and video on the SoC with only one status register that isn't 64bit aligned so 64bit I/O would result in an unaligned access exception. Switch to 32bit I/O in preparation for supporting these resets too. Signed-off-by: Emil Renner Berthing --- .../reset/starfive/reset-starfive-jh7100.c | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/drivers/reset/starfive/reset-starfive-jh7100.c b/drivers/reset/starfive/reset-starfive-jh7100.c index fc44b2fb3e031..7563d317f5c8e 100644 --- a/drivers/reset/starfive/reset-starfive-jh7100.c +++ b/drivers/reset/starfive/reset-starfive-jh7100.c @@ -34,16 +34,16 @@ * lines don't though, so store the expected value of the status registers when * all lines are asserted. */ -static const u64 jh7100_reset_asserted[2] = { +static const u32 jh7100_reset_asserted[4] = { /* STATUS0 */ - BIT_ULL_MASK(JH7100_RST_U74) | - BIT_ULL_MASK(JH7100_RST_VP6_DRESET) | - BIT_ULL_MASK(JH7100_RST_VP6_BRESET) | + BIT(JH7100_RST_U74 % 32) | + BIT(JH7100_RST_VP6_DRESET % 32) | + BIT(JH7100_RST_VP6_BRESET % 32), /* STATUS1 */ - BIT_ULL_MASK(JH7100_RST_HIFI4_DRESET) | - BIT_ULL_MASK(JH7100_RST_HIFI4_BRESET), + BIT(JH7100_RST_HIFI4_DRESET % 32) | + BIT(JH7100_RST_HIFI4_BRESET % 32), /* STATUS2 */ - BIT_ULL_MASK(JH7100_RST_E24) | + BIT(JH7100_RST_E24 % 32), /* STATUS3 */ 0, }; @@ -65,12 +65,12 @@ static int jh7100_reset_update(struct reset_controller_dev *rcdev, unsigned long id, bool assert) { struct jh7100_reset *data = jh7100_reset_from(rcdev); - unsigned long offset = BIT_ULL_WORD(id); - u64 mask = BIT_ULL_MASK(id); - void __iomem *reg_assert = data->base + JH7100_RESET_ASSERT0 + offset * sizeof(u64); - void __iomem *reg_status = data->base + JH7100_RESET_STATUS0 + offset * sizeof(u64); - u64 done = jh7100_reset_asserted[offset] & mask; - u64 value; + unsigned long offset = id / 32; + u32 mask = BIT(id % 32); + void __iomem *reg_assert = data->base + JH7100_RESET_ASSERT0 + offset * sizeof(u32); + void __iomem *reg_status = data->base + JH7100_RESET_STATUS0 + offset * sizeof(u32); + u32 done = jh7100_reset_asserted[offset] & mask; + u32 value; unsigned long flags; int ret; @@ -79,15 +79,15 @@ static int jh7100_reset_update(struct reset_controller_dev *rcdev, spin_lock_irqsave(&data->lock, flags); - value = readq(reg_assert); + value = readl(reg_assert); if (assert) value |= mask; else value &= ~mask; - writeq(value, reg_assert); + writel(value, reg_assert); /* if the associated clock is gated, deasserting might otherwise hang forever */ - ret = readq_poll_timeout_atomic(reg_status, value, (value & mask) == done, 0, 1000); + ret = readl_poll_timeout_atomic(reg_status, value, (value & mask) == done, 0, 1000); spin_unlock_irqrestore(&data->lock, flags); return ret; @@ -121,10 +121,10 @@ static int jh7100_reset_status(struct reset_controller_dev *rcdev, unsigned long id) { struct jh7100_reset *data = jh7100_reset_from(rcdev); - unsigned long offset = BIT_ULL_WORD(id); - u64 mask = BIT_ULL_MASK(id); - void __iomem *reg_status = data->base + JH7100_RESET_STATUS0 + offset * sizeof(u64); - u64 value = readq(reg_status); + unsigned long offset = id / 32; + u32 mask = BIT(id % 32); + void __iomem *reg_status = data->base + JH7100_RESET_STATUS0 + offset * sizeof(u32); + u32 value = readl(reg_status); return !((value ^ jh7100_reset_asserted[offset]) & mask); } From 142ffc238d291d7b57fc84fbd2a9b81c23353c22 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Sat, 20 Nov 2021 19:30:49 +0100 Subject: [PATCH 36/80] reset: starfive: Add JH7100 audio reset driver The audio resets are almost identical to the system resets, there are just fewer of them. So factor out and export a generic probe function, so most of the reset controller implementation can be shared. Signed-off-by: Emil Renner Berthing --- MAINTAINERS | 8 +-- drivers/reset/starfive/Kconfig | 7 +++ drivers/reset/starfive/Makefile | 1 + .../starfive/reset-starfive-jh7100-audio.c | 58 +++++++++++++++++++ .../reset/starfive/reset-starfive-jh7100.c | 37 ++++++++---- .../reset/starfive/reset-starfive-jh7100.h | 16 +++++ 6 files changed, 112 insertions(+), 15 deletions(-) create mode 100644 drivers/reset/starfive/reset-starfive-jh7100-audio.c create mode 100644 drivers/reset/starfive/reset-starfive-jh7100.h diff --git a/MAINTAINERS b/MAINTAINERS index ada639d6a0106..553f27c892460 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -18171,12 +18171,12 @@ F: Documentation/devicetree/bindings/pinctrl/starfive,jh7100-pinctrl.yaml F: drivers/pinctrl/pinctrl-starfive.c F: include/dt-bindings/pinctrl/pinctrl-starfive.h -STARFIVE JH7100 RESET CONTROLLER DRIVER +STARFIVE JH7100 RESET CONTROLLER DRIVERS M: Emil Renner Berthing S: Maintained -F: Documentation/devicetree/bindings/reset/starfive,jh7100-reset.yaml -F: drivers/reset/starfive/reset-starfive-jh7100.c -F: include/dt-bindings/reset/starfive-jh7100.h +F: Documentation/devicetree/bindings/reset/starfive,jh7100-*.yaml +F: drivers/reset/starfive/reset-starfive-jh7100* +F: include/dt-bindings/reset/starfive-jh7100*.h STATIC BRANCH/CALL M: Peter Zijlstra diff --git a/drivers/reset/starfive/Kconfig b/drivers/reset/starfive/Kconfig index cddebdba71773..f8d463196119f 100644 --- a/drivers/reset/starfive/Kconfig +++ b/drivers/reset/starfive/Kconfig @@ -6,3 +6,10 @@ config RESET_STARFIVE_JH7100 default SOC_STARFIVE help This enables the reset controller driver for the StarFive JH7100 SoC. + +config RESET_STARFIVE_JH7100_AUDIO + tristate "StarFive JH7100 Audio Reset Driver" + depends on RESET_STARFIVE_JH7100 + default SOC_STARFIVE + help + This enables the audio reset driver for the StarFive JH7100 SoC. diff --git a/drivers/reset/starfive/Makefile b/drivers/reset/starfive/Makefile index 670d049423f54..d3a55a75dd0f8 100644 --- a/drivers/reset/starfive/Makefile +++ b/drivers/reset/starfive/Makefile @@ -1,2 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_RESET_STARFIVE_JH7100) += reset-starfive-jh7100.o +obj-$(CONFIG_RESET_STARFIVE_JH7100_AUDIO) += reset-starfive-jh7100-audio.o diff --git a/drivers/reset/starfive/reset-starfive-jh7100-audio.c b/drivers/reset/starfive/reset-starfive-jh7100-audio.c new file mode 100644 index 0000000000000..bae8f8c90d281 --- /dev/null +++ b/drivers/reset/starfive/reset-starfive-jh7100-audio.c @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Audio reset driver for the StarFive JH7100 SoC + * + * Copyright (C) 2021 Emil Renner Berthing + */ + +#include +#include +#include +#include + +#include + +#include "reset-starfive-jh7100.h" + +/* register offsets */ +#define JH7100_AUDRST_ASSERT0 0x00 +#define JH7100_AUDRST_STATUS0 0x04 + +/* + * Writing a 1 to the n'th bit of the ASSERT register asserts + * line n, and writing a 0 deasserts the same line. + * Most reset lines have their status inverted so a 0 bit in the STATUS + * register means the line is asserted and a 1 means it's deasserted. A few + * lines don't though, so store the expected value of the status registers when + * all lines are asserted. + */ +static const u32 jh7100_audrst_asserted[1] = { + BIT(JH7100_AUDRST_USB_AXI) | + BIT(JH7100_AUDRST_USB_PWRUP_RST_N) | + BIT(JH7100_AUDRST_USB_PONRST) +}; + +static int jh7100_audrst_probe(struct platform_device *pdev) +{ + return reset_starfive_jh7100_generic_probe(pdev, jh7100_audrst_asserted, + JH7100_AUDRST_STATUS0, JH7100_AUDRSTN_END); +} + +static const struct of_device_id jh7100_audrst_dt_ids[] = { + { .compatible = "starfive,jh7100-audrst" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, jh7100_audrst_dt_ids); + +static struct platform_driver jh7100_audrst_driver = { + .probe = jh7100_audrst_probe, + .driver = { + .name = "jh7100-reset-audio", + .of_match_table = jh7100_audrst_dt_ids, + }, +}; +module_platform_driver(jh7100_audrst_driver); + +MODULE_AUTHOR("Emil Renner Berthing"); +MODULE_DESCRIPTION("StarFive JH7100 audio reset driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/reset/starfive/reset-starfive-jh7100.c b/drivers/reset/starfive/reset-starfive-jh7100.c index 7563d317f5c8e..8e8733908c702 100644 --- a/drivers/reset/starfive/reset-starfive-jh7100.c +++ b/drivers/reset/starfive/reset-starfive-jh7100.c @@ -16,6 +16,8 @@ #include +#include "reset-starfive-jh7100.h" + /* register offsets */ #define JH7100_RESET_ASSERT0 0x00 #define JH7100_RESET_ASSERT1 0x04 @@ -52,7 +54,9 @@ struct jh7100_reset { struct reset_controller_dev rcdev; /* protect registers against concurrent read-modify-write */ spinlock_t lock; - void __iomem *base; + void __iomem *assert; + void __iomem *status; + const u32 *asserted; }; static inline struct jh7100_reset * @@ -67,9 +71,9 @@ static int jh7100_reset_update(struct reset_controller_dev *rcdev, struct jh7100_reset *data = jh7100_reset_from(rcdev); unsigned long offset = id / 32; u32 mask = BIT(id % 32); - void __iomem *reg_assert = data->base + JH7100_RESET_ASSERT0 + offset * sizeof(u32); - void __iomem *reg_status = data->base + JH7100_RESET_STATUS0 + offset * sizeof(u32); - u32 done = jh7100_reset_asserted[offset] & mask; + void __iomem *reg_assert = data->assert + offset * sizeof(u32); + void __iomem *reg_status = data->status + offset * sizeof(u32); + u32 done = data->asserted[offset] & mask; u32 value; unsigned long flags; int ret; @@ -123,10 +127,10 @@ static int jh7100_reset_status(struct reset_controller_dev *rcdev, struct jh7100_reset *data = jh7100_reset_from(rcdev); unsigned long offset = id / 32; u32 mask = BIT(id % 32); - void __iomem *reg_status = data->base + JH7100_RESET_STATUS0 + offset * sizeof(u32); + void __iomem *reg_status = data->status + offset * sizeof(u32); u32 value = readl(reg_status); - return !((value ^ jh7100_reset_asserted[offset]) & mask); + return !((value ^ data->asserted[offset]) & mask); } static const struct reset_control_ops jh7100_reset_ops = { @@ -136,7 +140,8 @@ static const struct reset_control_ops jh7100_reset_ops = { .status = jh7100_reset_status, }; -static int __init jh7100_reset_probe(struct platform_device *pdev) +int reset_starfive_jh7100_generic_probe(struct platform_device *pdev, const u32 *asserted, + unsigned int status_offset, unsigned int nr_resets) { struct jh7100_reset *data; @@ -144,19 +149,29 @@ static int __init jh7100_reset_probe(struct platform_device *pdev) if (!data) return -ENOMEM; - data->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(data->base)) - return PTR_ERR(data->base); + data->assert = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(data->assert)) + return PTR_ERR(data->assert); data->rcdev.ops = &jh7100_reset_ops; data->rcdev.owner = THIS_MODULE; - data->rcdev.nr_resets = JH7100_RSTN_END; + data->rcdev.nr_resets = nr_resets; data->rcdev.dev = &pdev->dev; data->rcdev.of_node = pdev->dev.of_node; + spin_lock_init(&data->lock); + data->status = data->assert + status_offset; + data->asserted = asserted; return devm_reset_controller_register(&pdev->dev, &data->rcdev); } +EXPORT_SYMBOL_GPL(reset_starfive_jh7100_generic_probe); + +static int __init jh7100_reset_probe(struct platform_device *pdev) +{ + return reset_starfive_jh7100_generic_probe(pdev, jh7100_reset_asserted, + JH7100_RESET_STATUS0, JH7100_RSTN_END); +} static const struct of_device_id jh7100_reset_dt_ids[] = { { .compatible = "starfive,jh7100-reset" }, diff --git a/drivers/reset/starfive/reset-starfive-jh7100.h b/drivers/reset/starfive/reset-starfive-jh7100.h new file mode 100644 index 0000000000000..ee8f3e3b1644d --- /dev/null +++ b/drivers/reset/starfive/reset-starfive-jh7100.h @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2021 Emil Renner Berthing + */ + +#ifndef _RESET_STARFIVE_JH7100_H_ +#define _RESET_STARFIVE_JH7100_H_ + +#include + +int reset_starfive_jh7100_generic_probe(struct platform_device *pdev, + const u32 *asserted, + unsigned int status_offset, + unsigned int nr_resets); + +#endif From 43977e916cd27e071653fb272d4db69a62ddbb43 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Sat, 20 Nov 2021 21:33:08 +0100 Subject: [PATCH 37/80] RISC-V: Add StarFive JH7100 audio reset node Add device tree node for the audio resets on the StarFive JH7100 RISC-V SoC. Signed-off-by: Emil Renner Berthing --- arch/riscv/boot/dts/starfive/jh7100.dtsi | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/riscv/boot/dts/starfive/jh7100.dtsi b/arch/riscv/boot/dts/starfive/jh7100.dtsi index 31b4ccc282e0b..d76a670986206 100644 --- a/arch/riscv/boot/dts/starfive/jh7100.dtsi +++ b/arch/riscv/boot/dts/starfive/jh7100.dtsi @@ -131,6 +131,12 @@ #clock-cells = <1>; }; + audrst: reset-controller@10490000 { + compatible = "starfive,jh7100-audrst"; + reg = <0x0 0x10490000 0x0 0x10000>; + #reset-cells = <1>; + }; + clkgen: clock-controller@11800000 { compatible = "starfive,jh7100-clkgen"; reg = <0x0 0x11800000 0x0 0x10000>; From e31cc37d0fabaf7347282b489480a5c236d248ea Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Thu, 14 Oct 2021 20:35:43 +0200 Subject: [PATCH 38/80] clk: starfive: jh7100: Keep more clocks alive Signed-off-by: Emil Renner Berthing --- drivers/clk/starfive/clk-starfive-jh7100.c | 38 +++++++++++----------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/drivers/clk/starfive/clk-starfive-jh7100.c b/drivers/clk/starfive/clk-starfive-jh7100.c index 691aeebc70927..75942914e02f7 100644 --- a/drivers/clk/starfive/clk-starfive-jh7100.c +++ b/drivers/clk/starfive/clk-starfive-jh7100.c @@ -99,9 +99,9 @@ static const struct jh7100_clk_data jh7100_clk_data[] __initconst = { JH7100_GATE(JH7100_CLK_DMA2PNOC_AXI, "dma2pnoc_axi", 0, JH7100_CLK_CPU_AXI), JH7100_GATE(JH7100_CLK_SGDMA2P_AHB, "sgdma2p_ahb", 0, JH7100_CLK_AHB_BUS), JH7100__DIV(JH7100_CLK_DLA_BUS, "dla_bus", 4, JH7100_CLK_DLA_ROOT), - JH7100_GATE(JH7100_CLK_DLA_AXI, "dla_axi", 0, JH7100_CLK_DLA_BUS), - JH7100_GATE(JH7100_CLK_DLANOC_AXI, "dlanoc_axi", 0, JH7100_CLK_DLA_BUS), - JH7100_GATE(JH7100_CLK_DLA_APB, "dla_apb", 0, JH7100_CLK_APB1_BUS), + JH7100_GATE(JH7100_CLK_DLA_AXI, "dla_axi", CLK_IGNORE_UNUSED, JH7100_CLK_DLA_BUS), + JH7100_GATE(JH7100_CLK_DLANOC_AXI, "dlanoc_axi", CLK_IGNORE_UNUSED, JH7100_CLK_DLA_BUS), + JH7100_GATE(JH7100_CLK_DLA_APB, "dla_apb", CLK_IGNORE_UNUSED, JH7100_CLK_APB1_BUS), JH7100_GDIV(JH7100_CLK_VP6_CORE, "vp6_core", 0, 4, JH7100_CLK_DSP_ROOT_DIV), JH7100__DIV(JH7100_CLK_VP6BUS_SRC, "vp6bus_src", 4, JH7100_CLK_DSP_ROOT), JH7100_GDIV(JH7100_CLK_VP6_AXI, "vp6_axi", 0, 4, JH7100_CLK_VP6BUS_SRC), @@ -163,11 +163,11 @@ static const struct jh7100_clk_data jh7100_clk_data[] __initconst = { JH7100_GATE(JH7100_CLK_DMA1P_AXI, "dma1p_axi", 0, JH7100_CLK_SGDMA1P_BUS), JH7100_GDIV(JH7100_CLK_X2C_AXI, "x2c_axi", CLK_IS_CRITICAL, 8, JH7100_CLK_CPUNBUS_ROOT_DIV), JH7100__DIV(JH7100_CLK_USB_BUS, "usb_bus", 8, JH7100_CLK_CPUNBUS_ROOT_DIV), - JH7100_GATE(JH7100_CLK_USB_AXI, "usb_axi", 0, JH7100_CLK_USB_BUS), - JH7100_GATE(JH7100_CLK_USBNOC_AXI, "usbnoc_axi", 0, JH7100_CLK_USB_BUS), + JH7100_GATE(JH7100_CLK_USB_AXI, "usb_axi", CLK_IGNORE_UNUSED, JH7100_CLK_USB_BUS), + JH7100_GATE(JH7100_CLK_USBNOC_AXI, "usbnoc_axi", CLK_IGNORE_UNUSED, JH7100_CLK_USB_BUS), JH7100__DIV(JH7100_CLK_USBPHY_ROOTDIV, "usbphy_rootdiv", 4, JH7100_CLK_GMACUSB_ROOT), - JH7100_GDIV(JH7100_CLK_USBPHY_125M, "usbphy_125m", 0, 8, JH7100_CLK_USBPHY_ROOTDIV), - JH7100_GDIV(JH7100_CLK_USBPHY_PLLDIV25M, "usbphy_plldiv25m", 0, 32, JH7100_CLK_USBPHY_ROOTDIV), + JH7100_GDIV(JH7100_CLK_USBPHY_125M, "usbphy_125m", CLK_IGNORE_UNUSED, 8, JH7100_CLK_USBPHY_ROOTDIV), + JH7100_GDIV(JH7100_CLK_USBPHY_PLLDIV25M, "usbphy_plldiv25m", CLK_IGNORE_UNUSED, 32, JH7100_CLK_USBPHY_ROOTDIV), JH7100__MUX(JH7100_CLK_USBPHY_25M, "usbphy_25m", 2, JH7100_CLK_OSC_SYS, JH7100_CLK_USBPHY_PLLDIV25M), @@ -185,23 +185,23 @@ static const struct jh7100_clk_data jh7100_clk_data[] __initconst = { JH7100__DIV(JH7100_CLK_VIN_BUS, "vin_bus", 8, JH7100_CLK_VIN_SRC), JH7100_GATE(JH7100_CLK_VIN_AXI, "vin_axi", 0, JH7100_CLK_VIN_BUS), JH7100_GATE(JH7100_CLK_VINNOC_AXI, "vinnoc_axi", 0, JH7100_CLK_VIN_BUS), - JH7100_GDIV(JH7100_CLK_VOUT_SRC, "vout_src", 0, 4, JH7100_CLK_VOUT_ROOT), + JH7100_GDIV(JH7100_CLK_VOUT_SRC, "vout_src", CLK_IGNORE_UNUSED, 4, JH7100_CLK_VOUT_ROOT), JH7100__DIV(JH7100_CLK_DISPBUS_SRC, "dispbus_src", 4, JH7100_CLK_VOUTBUS_ROOT), JH7100__DIV(JH7100_CLK_DISP_BUS, "disp_bus", 4, JH7100_CLK_DISPBUS_SRC), - JH7100_GATE(JH7100_CLK_DISP_AXI, "disp_axi", 0, JH7100_CLK_DISP_BUS), - JH7100_GATE(JH7100_CLK_DISPNOC_AXI, "dispnoc_axi", 0, JH7100_CLK_DISP_BUS), + JH7100_GATE(JH7100_CLK_DISP_AXI, "disp_axi", CLK_IGNORE_UNUSED, JH7100_CLK_DISP_BUS), + JH7100_GATE(JH7100_CLK_DISPNOC_AXI, "dispnoc_axi", CLK_IGNORE_UNUSED, JH7100_CLK_DISP_BUS), JH7100_GATE(JH7100_CLK_SDIO0_AHB, "sdio0_ahb", 0, JH7100_CLK_AHB_BUS), JH7100_GDIV(JH7100_CLK_SDIO0_CCLKINT, "sdio0_cclkint", 0, 24, JH7100_CLK_PERH0_SRC), JH7100__INV(JH7100_CLK_SDIO0_CCLKINT_INV, "sdio0_cclkint_inv", JH7100_CLK_SDIO0_CCLKINT), JH7100_GATE(JH7100_CLK_SDIO1_AHB, "sdio1_ahb", 0, JH7100_CLK_AHB_BUS), JH7100_GDIV(JH7100_CLK_SDIO1_CCLKINT, "sdio1_cclkint", 0, 24, JH7100_CLK_PERH1_SRC), JH7100__INV(JH7100_CLK_SDIO1_CCLKINT_INV, "sdio1_cclkint_inv", JH7100_CLK_SDIO1_CCLKINT), - JH7100_GATE(JH7100_CLK_GMAC_AHB, "gmac_ahb", 0, JH7100_CLK_AHB_BUS), + JH7100_GATE(JH7100_CLK_GMAC_AHB, "gmac_ahb", CLK_IGNORE_UNUSED, JH7100_CLK_AHB_BUS), JH7100__DIV(JH7100_CLK_GMAC_ROOT_DIV, "gmac_root_div", 8, JH7100_CLK_GMACUSB_ROOT), - JH7100_GDIV(JH7100_CLK_GMAC_PTP_REF, "gmac_ptp_refclk", 0, 31, JH7100_CLK_GMAC_ROOT_DIV), - JH7100_GDIV(JH7100_CLK_GMAC_GTX, "gmac_gtxclk", 0, 255, JH7100_CLK_GMAC_ROOT_DIV), - JH7100_GDIV(JH7100_CLK_GMAC_RMII_TX, "gmac_rmii_txclk", 0, 8, JH7100_CLK_GMAC_RMII_REF), - JH7100_GDIV(JH7100_CLK_GMAC_RMII_RX, "gmac_rmii_rxclk", 0, 8, JH7100_CLK_GMAC_RMII_REF), + JH7100_GDIV(JH7100_CLK_GMAC_PTP_REF, "gmac_ptp_refclk", CLK_IGNORE_UNUSED, 31, JH7100_CLK_GMAC_ROOT_DIV), + JH7100_GDIV(JH7100_CLK_GMAC_GTX, "gmac_gtxclk", CLK_IGNORE_UNUSED, 255, JH7100_CLK_GMAC_ROOT_DIV), + JH7100_GDIV(JH7100_CLK_GMAC_RMII_TX, "gmac_rmii_txclk", CLK_IGNORE_UNUSED, 8, JH7100_CLK_GMAC_RMII_REF), + JH7100_GDIV(JH7100_CLK_GMAC_RMII_RX, "gmac_rmii_rxclk", CLK_IGNORE_UNUSED, 8, JH7100_CLK_GMAC_RMII_REF), JH7100__MUX(JH7100_CLK_GMAC_TX, "gmac_tx", 3, JH7100_CLK_GMAC_GTX, JH7100_CLK_GMAC_TX_INV, @@ -211,8 +211,8 @@ static const struct jh7100_clk_data jh7100_clk_data[] __initconst = { JH7100_CLK_GMAC_GR_MII_RX, JH7100_CLK_GMAC_RMII_RX), JH7100__INV(JH7100_CLK_GMAC_RX_INV, "gmac_rx_inv", JH7100_CLK_GMAC_RX_PRE), - JH7100_GATE(JH7100_CLK_GMAC_RMII, "gmac_rmii", 0, JH7100_CLK_GMAC_RMII_REF), - JH7100_GDIV(JH7100_CLK_GMAC_TOPHYREF, "gmac_tophyref", 0, 127, JH7100_CLK_GMAC_ROOT_DIV), + JH7100_GATE(JH7100_CLK_GMAC_RMII, "gmac_rmii", CLK_IGNORE_UNUSED, JH7100_CLK_GMAC_RMII_REF), + JH7100_GDIV(JH7100_CLK_GMAC_TOPHYREF, "gmac_tophyref", CLK_IGNORE_UNUSED, 127, JH7100_CLK_GMAC_ROOT_DIV), JH7100_GATE(JH7100_CLK_SPI2AHB_AHB, "spi2ahb_ahb", 0, JH7100_CLK_AHB_BUS), JH7100_GDIV(JH7100_CLK_SPI2AHB_CORE, "spi2ahb_core", 0, 31, JH7100_CLK_PERH0_SRC), JH7100_GATE(JH7100_CLK_EZMASTER_AHB, "ezmaster_ahb", 0, JH7100_CLK_AHB_BUS), @@ -225,7 +225,7 @@ static const struct jh7100_clk_data jh7100_clk_data[] __initconst = { JH7100_GATE(JH7100_CLK_AES, "aes_clk", 0, JH7100_CLK_SEC_AHB), JH7100_GATE(JH7100_CLK_SHA, "sha_clk", 0, JH7100_CLK_SEC_AHB), JH7100_GATE(JH7100_CLK_PKA, "pka_clk", 0, JH7100_CLK_SEC_AHB), - JH7100_GATE(JH7100_CLK_TRNG_APB, "trng_apb", 0, JH7100_CLK_APB1_BUS), + JH7100_GATE(JH7100_CLK_TRNG_APB, "trng_apb", CLK_IGNORE_UNUSED, JH7100_CLK_APB1_BUS), JH7100_GATE(JH7100_CLK_OTP_APB, "otp_apb", 0, JH7100_CLK_APB1_BUS), JH7100_GATE(JH7100_CLK_UART0_APB, "uart0_apb", 0, JH7100_CLK_APB1_BUS), JH7100_GDIV(JH7100_CLK_UART0_CORE, "uart0_core", 0, 63, JH7100_CLK_PERH1_SRC), @@ -262,7 +262,7 @@ static const struct jh7100_clk_data jh7100_clk_data[] __initconst = { JH7100_GDIV(JH7100_CLK_TIMER5_CORE, "timer5_coreclk", 0, 63, JH7100_CLK_PERH0_SRC), JH7100_GDIV(JH7100_CLK_TIMER6_CORE, "timer6_coreclk", 0, 63, JH7100_CLK_PERH0_SRC), JH7100_GATE(JH7100_CLK_VP6INTC_APB, "vp6intc_apb", 0, JH7100_CLK_APB2_BUS), - JH7100_GATE(JH7100_CLK_PWM_APB, "pwm_apb", 0, JH7100_CLK_APB2_BUS), + JH7100_GATE(JH7100_CLK_PWM_APB, "pwm_apb", CLK_IGNORE_UNUSED, JH7100_CLK_APB2_BUS), JH7100_GATE(JH7100_CLK_MSI_APB, "msi_apb", 0, JH7100_CLK_APB2_BUS), JH7100_GATE(JH7100_CLK_TEMP_APB, "temp_apb", 0, JH7100_CLK_APB2_BUS), JH7100_GDIV(JH7100_CLK_TEMP_SENSE, "temp_sense", 0, 31, JH7100_CLK_OSC_SYS), From a0748683c4206cefbd85760ba66bebab4240aff6 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Sat, 17 Jul 2021 21:50:38 +0200 Subject: [PATCH 39/80] pinctrl: starfive: Reset pinmux settings Current u-boot doesn't seem to take into account that some GPIOs are configured as inputs/outputs of certain peripherals on power-up. This means it ends up configuring some GPIOs as inputs to more than one peripheral which the documentation explicitly says is illegal. Similarly it also ends up configuring more than one GPIO as output of the same peripheral. While not explicitly mentioned by the documentation this also seems like a bad idea. The easiest way to remedy this mess is to just disconnect all GPIOs from peripherals and have our pinmux configuration set everything up properly. This, however, means that we'd disconnect the serial console from its pins for a while, so add a device tree property to keep certain GPIOs from being reset. Signed-off-by: Emil Renner Berthing --- .../pinctrl/starfive,jh7100-pinctrl.yaml | 4 ++ drivers/pinctrl/pinctrl-starfive.c | 66 +++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/Documentation/devicetree/bindings/pinctrl/starfive,jh7100-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/starfive,jh7100-pinctrl.yaml index 92963604422f4..3d89594928d78 100644 --- a/Documentation/devicetree/bindings/pinctrl/starfive,jh7100-pinctrl.yaml +++ b/Documentation/devicetree/bindings/pinctrl/starfive,jh7100-pinctrl.yaml @@ -88,6 +88,10 @@ properties: $ref: /schemas/types.yaml#/definitions/uint32 enum: [0, 1, 2, 3, 4, 5, 6] + starfive,keep-gpiomux: + description: Keep pinmux for these GPIOs from being reset at boot. + $ref: /schemas/types.yaml#/definitions/uint32-array + required: - compatible - reg diff --git a/drivers/pinctrl/pinctrl-starfive.c b/drivers/pinctrl/pinctrl-starfive.c index 0b912152a405a..7147f3e4446bd 100644 --- a/drivers/pinctrl/pinctrl-starfive.c +++ b/drivers/pinctrl/pinctrl-starfive.c @@ -200,6 +200,10 @@ static u16 starfive_drive_strength_from_max_mA(u32 i) return (clamp(i, 14U, 63U) - 14) / 7; } +static bool keepmux; +module_param(keepmux, bool, 0644); +MODULE_PARM_DESC(keepmux, "Keep pinmux settings from previous boot stage"); + struct starfive_pinctrl { struct gpio_chip gc; struct pinctrl_gpio_range gpios; @@ -1212,6 +1216,65 @@ static void starfive_disable_clock(void *data) clk_disable_unprepare(data); } +#define GPI_END (GPI_USB_OVER_CURRENT + 1) +static void starfive_pinmux_reset(struct starfive_pinctrl *sfp) +{ + static const DECLARE_BITMAP(defaults, GPI_END) = { + BIT_MASK(GPI_I2C0_PAD_SCK_IN) | + BIT_MASK(GPI_I2C0_PAD_SDA_IN) | + BIT_MASK(GPI_I2C1_PAD_SCK_IN) | + BIT_MASK(GPI_I2C1_PAD_SDA_IN) | + BIT_MASK(GPI_I2C2_PAD_SCK_IN) | + BIT_MASK(GPI_I2C2_PAD_SDA_IN) | + BIT_MASK(GPI_I2C3_PAD_SCK_IN) | + BIT_MASK(GPI_I2C3_PAD_SDA_IN) | + BIT_MASK(GPI_SDIO0_PAD_CARD_DETECT_N) | + + BIT_MASK(GPI_SDIO1_PAD_CARD_DETECT_N) | + BIT_MASK(GPI_SPI0_PAD_SS_IN_N) | + BIT_MASK(GPI_SPI1_PAD_SS_IN_N) | + BIT_MASK(GPI_SPI2_PAD_SS_IN_N) | + BIT_MASK(GPI_SPI2AHB_PAD_SS_N) | + BIT_MASK(GPI_SPI3_PAD_SS_IN_N), + + BIT_MASK(GPI_UART0_PAD_SIN) | + BIT_MASK(GPI_UART1_PAD_SIN) | + BIT_MASK(GPI_UART2_PAD_SIN) | + BIT_MASK(GPI_UART3_PAD_SIN) | + BIT_MASK(GPI_USB_OVER_CURRENT) + }; + DECLARE_BITMAP(keep, NR_GPIOS) = {}; + struct device_node *np = sfp->gc.parent->of_node; + int len = of_property_count_u32_elems(np, "starfive,keep-gpiomux"); + int i; + + for (i = 0; i < len; i++) { + u32 gpio; + + of_property_read_u32_index(np, "starfive,keep-gpiomux", i, &gpio); + if (gpio < NR_GPIOS) + set_bit(gpio, keep); + } + + for (i = 0; i < NR_GPIOS; i++) { + if (test_bit(i, keep)) + continue; + + writel_relaxed(GPO_DISABLE, sfp->base + GPON_DOEN_CFG + 8 * i); + writel_relaxed(GPO_LOW, sfp->base + GPON_DOUT_CFG + 8 * i); + } + + for (i = 0; i < GPI_END; i++) { + void __iomem *reg = sfp->base + GPI_CFG_OFFSET + 4 * i; + u32 din = readl_relaxed(reg); + + if (din >= 2 && din < (NR_GPIOS + 2) && test_bit(din - 2, keep)) + continue; + + writel_relaxed(test_bit(i, defaults), reg); + } +} + static int starfive_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -1272,6 +1335,9 @@ static int starfive_probe(struct platform_device *pdev) writel(value, sfp->padctl + IO_PADSHARE_SEL); } + if (!keepmux) + starfive_pinmux_reset(sfp); + value = readl(sfp->padctl + IO_PADSHARE_SEL); switch (value) { case 0: From b2761237196343af041911c211234a9e86ad4818 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Tue, 30 Nov 2021 14:26:21 +0100 Subject: [PATCH 40/80] serial: 8250_dw: Use device tree match data ..rather than multiple calls to of_device_is_compatible(). Signed-off-by: Emil Renner Berthing --- drivers/tty/serial/8250/8250_dw.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index 1769808031c52..f564a019a7be1 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -37,6 +37,11 @@ /* DesignWare specific register fields */ #define DW_UART_MCR_SIRE BIT(6) +/* Quirks */ +#define DW_UART_QUIRK_OCTEON BIT(0) +#define DW_UART_QUIRK_ARMADA_38X BIT(1) +#define DW_UART_QUIRK_SKIP_SET_RATE BIT(2) + struct dw8250_data { struct dw8250_port_data data; @@ -389,6 +394,7 @@ static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data) struct device_node *np = p->dev->of_node; if (np) { + unsigned long quirks = (unsigned long)of_device_get_match_data(p->dev); int id; /* get index of serial line, if found in DT aliases */ @@ -396,7 +402,7 @@ static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data) if (id >= 0) p->line = id; #ifdef CONFIG_64BIT - if (of_device_is_compatible(np, "cavium,octeon-3860-uart")) { + if (quirks & DW_UART_QUIRK_OCTEON) { p->serial_in = dw8250_serial_inq; p->serial_out = dw8250_serial_outq; p->flags = UPF_SKIP_TEST | UPF_SHARE_IRQ | UPF_FIXED_TYPE; @@ -412,9 +418,9 @@ static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data) p->serial_out = dw8250_serial_out32be; } - if (of_device_is_compatible(np, "marvell,armada-38x-uart")) + if (quirks & DW_UART_QUIRK_ARMADA_38X) p->serial_out = dw8250_serial_out38x; - if (of_device_is_compatible(np, "starfive,jh7100-uart")) + if (quirks & DW_UART_QUIRK_SKIP_SET_RATE) p->set_termios = dw8250_do_set_termios; } else if (acpi_dev_present("APMC0D08", NULL, -1)) { @@ -695,10 +701,10 @@ static const struct dev_pm_ops dw8250_pm_ops = { static const struct of_device_id dw8250_of_match[] = { { .compatible = "snps,dw-apb-uart" }, - { .compatible = "cavium,octeon-3860-uart" }, - { .compatible = "marvell,armada-38x-uart" }, + { .compatible = "cavium,octeon-3860-uart", .data = (void *)DW_UART_QUIRK_OCTEON }, + { .compatible = "marvell,armada-38x-uart", .data = (void *)DW_UART_QUIRK_ARMADA_38X }, { .compatible = "renesas,rzn1-uart" }, - { .compatible = "starfive,jh7100-uart" }, + { .compatible = "starfive,jh7100-uart", .data = (void *)DW_UART_QUIRK_SKIP_SET_RATE }, { /* Sentinel */ } }; MODULE_DEVICE_TABLE(of, dw8250_of_match); From 74c674b12a26c06e9fb77e1e5e135fbca420e1d1 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Thu, 14 Oct 2021 20:56:54 +0200 Subject: [PATCH 41/80] serial: 8250_dw: Add starfive,jh7100-hsuart compatible This adds a compatible for the high speed UARTs on the StarFive JH7100 RISC-V SoC. Just like the regular uarts we also need to keep the input clocks at their default rate and rely only on the divisor in the UART. Signed-off-by: Emil Renner Berthing --- drivers/tty/serial/8250/8250_dw.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index f564a019a7be1..96a62e95726b6 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -704,6 +704,7 @@ static const struct of_device_id dw8250_of_match[] = { { .compatible = "cavium,octeon-3860-uart", .data = (void *)DW_UART_QUIRK_OCTEON }, { .compatible = "marvell,armada-38x-uart", .data = (void *)DW_UART_QUIRK_ARMADA_38X }, { .compatible = "renesas,rzn1-uart" }, + { .compatible = "starfive,jh7100-hsuart", .data = (void *)DW_UART_QUIRK_SKIP_SET_RATE }, { .compatible = "starfive,jh7100-uart", .data = (void *)DW_UART_QUIRK_SKIP_SET_RATE }, { /* Sentinel */ } }; From 218d018fdc199e670f0ad393f3820dd7343832e6 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Sun, 6 Jun 2021 22:15:22 +0200 Subject: [PATCH 42/80] dt-bindings: hwmon: add starfive,jh7100-temp bindings Add bindings for the temperature sensor on the StarFive JH7100 SoC. Signed-off-by: Emil Renner Berthing Reviewed-by: Rob Herring --- .../bindings/hwmon/starfive,jh7100-temp.yaml | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 Documentation/devicetree/bindings/hwmon/starfive,jh7100-temp.yaml diff --git a/Documentation/devicetree/bindings/hwmon/starfive,jh7100-temp.yaml b/Documentation/devicetree/bindings/hwmon/starfive,jh7100-temp.yaml new file mode 100644 index 0000000000000..3604cca50c25e --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/starfive,jh7100-temp.yaml @@ -0,0 +1,74 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/hwmon/starfive,jh7100-temp.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive JH7100 Temperature Sensor + +maintainers: + - Emil Renner Berthing + +description: | + StarFive Technology Co. JH7100 embedded temperature sensor + +properties: + compatible: + enum: + - starfive,jh7100-temp + + reg: + maxItems: 1 + + clocks: + minItems: 2 + maxItems: 2 + + clock-names: + items: + - const: "sense" + - const: "bus" + + '#thermal-sensor-cells': + const: 0 + + interrupts: + maxItems: 1 + + resets: + minItems: 2 + maxItems: 2 + + reset-names: + items: + - const: "sense" + - const: "bus" + +required: + - compatible + - reg + - clocks + - clock-names + - interrupts + - resets + - reset-names + +additionalProperties: false + +examples: + - | + #include + #include + + tmon: tmon@124a0000 { + compatible = "starfive,jh7100-temp"; + reg = <0x124a0000 0x10000>; + clocks = <&clkgen JH7100_CLK_TEMP_SENSE>, + <&clkgen JH7100_CLK_TEMP_APB>; + clock-names = "sense", "bus"; + #thermal-sensor-cells = <0>; + interrupts = <122>; + resets = <&rstgen JH7100_RSTN_TEMP_SENSE>, + <&rstgen JH7100_RSTN_TEMP_APB>; + reset-names = "sense", "bus"; + }; From b7cf049280d630d4fe24dd213e9b5e321ca3e521 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Sun, 6 Jun 2021 22:31:18 +0200 Subject: [PATCH 43/80] hwmon: (sfctemp) Add StarFive JH7100 temperature sensor Register definitions and conversion constants based on sfctemp driver by Samin in the StarFive 5.10 kernel. Signed-off-by: Emil Renner Berthing Signed-off-by: Samin Guo --- Documentation/hwmon/index.rst | 1 + Documentation/hwmon/sfctemp.rst | 32 +++ MAINTAINERS | 8 + drivers/hwmon/Kconfig | 10 + drivers/hwmon/Makefile | 1 + drivers/hwmon/sfctemp.c | 349 ++++++++++++++++++++++++++++++++ 6 files changed, 401 insertions(+) create mode 100644 Documentation/hwmon/sfctemp.rst create mode 100644 drivers/hwmon/sfctemp.c diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst index 7046bf1870d96..cf1323fdfd23e 100644 --- a/Documentation/hwmon/index.rst +++ b/Documentation/hwmon/index.rst @@ -167,6 +167,7 @@ Hardware Monitoring Kernel Drivers sch5627 sch5636 scpi-hwmon + sfctemp sht15 sht21 sht3x diff --git a/Documentation/hwmon/sfctemp.rst b/Documentation/hwmon/sfctemp.rst new file mode 100644 index 0000000000000..465edce2fea5e --- /dev/null +++ b/Documentation/hwmon/sfctemp.rst @@ -0,0 +1,32 @@ +.. SPDX-License-Identifier: GPL-2.0 + +Kernel driver sfctemp +===================== + +Supported chips: + - StarFive JH7100 + +Authors: + - Emil Renner Berthing + +Description +----------- + +This driver adds support for reading the built-in temperature sensor on the +JH7100 RISC-V SoC by StarFive Technology Co. Ltd. + +``sysfs`` interface +------------------- + +The temperature sensor can be enabled, disabled and queried via the standard +hwmon interface in sysfs under ``/sys/class/hwmon/hwmonX`` for some value of +``X``: + +================ ==== ============================================= +Name Perm Description +================ ==== ============================================= +temp1_enable RW Enable or disable temperature sensor. + Automatically enabled by the driver, + but may be disabled to save power. +temp1_input RO Temperature reading in milli-degrees Celsius. +================ ==== ============================================= diff --git a/MAINTAINERS b/MAINTAINERS index 553f27c892460..646a69f4426d3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17241,6 +17241,14 @@ L: netdev@vger.kernel.org S: Supported F: drivers/net/ethernet/sfc/ +SFCTEMP HWMON DRIVER +M: Emil Renner Berthing +L: linux-hwmon@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/hwmon/starfive,jh7100-temp.yaml +F: Documentation/hwmon/sfctemp.rst +F: drivers/hwmon/sfctemp.c + SFF/SFP/SFP+ MODULE SUPPORT M: Russell King L: netdev@vger.kernel.org diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 64bd3dfba2c4f..ebe0b6a8262dd 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1793,6 +1793,16 @@ config SENSORS_STTS751 This driver can also be built as a module. If so, the module will be called stts751. +config SENSORS_SFCTEMP + tristate "Starfive JH7100 temperature sensor" + depends on SOC_STARFIVE || COMPILE_TEST + help + If you say yes here you get support for temperature sensor + on the Starfive JH7100 SoC. + + This driver can also be built as a module. If so, the module + will be called sfctemp. + config SENSORS_SMM665 tristate "Summit Microelectronics SMM665" depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index baee6a8d4dd12..a8f50a2c821fe 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -169,6 +169,7 @@ obj-$(CONFIG_SENSORS_SBRMI) += sbrmi.o obj-$(CONFIG_SENSORS_SCH56XX_COMMON)+= sch56xx-common.o obj-$(CONFIG_SENSORS_SCH5627) += sch5627.o obj-$(CONFIG_SENSORS_SCH5636) += sch5636.o +obj-$(CONFIG_SENSORS_SFCTEMP) += sfctemp.o obj-$(CONFIG_SENSORS_SL28CPLD) += sl28cpld-hwmon.o obj-$(CONFIG_SENSORS_SHT15) += sht15.o obj-$(CONFIG_SENSORS_SHT21) += sht21.o diff --git a/drivers/hwmon/sfctemp.c b/drivers/hwmon/sfctemp.c new file mode 100644 index 0000000000000..d7af616b8d097 --- /dev/null +++ b/drivers/hwmon/sfctemp.c @@ -0,0 +1,349 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 Emil Renner Berthing + * Copyright (C) 2021 Samin Guo + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * TempSensor reset. The RSTN can be de-asserted once the analog core has + * powered up. Trst(min 100ns) + * 0:reset 1:de-assert + */ +#define SFCTEMP_RSTN BIT(0) + +/* + * TempSensor analog core power down. The analog core will be powered up + * Tpu(min 50us) after PD is de-asserted. RSTN should be held low until the + * analog core is powered up. + * 0:power up 1:power down + */ +#define SFCTEMP_PD BIT(1) + +/* + * TempSensor start conversion enable. + * 0:disable 1:enable + */ +#define SFCTEMP_RUN BIT(2) + +/* + * TempSensor conversion value output. + * Temp(C)=DOUT*Y/4094 - K + */ +#define SFCTEMP_DOUT_POS 16 +#define SFCTEMP_DOUT_MSK GENMASK(27, 16) + +/* DOUT to Celcius conversion constants */ +#define SFCTEMP_Y1000 237500L +#define SFCTEMP_Z 4094L +#define SFCTEMP_K1000 81100L + +struct sfctemp { + /* serialize access to hardware register and enabled below */ + struct mutex lock; + struct completion conversion_done; + void __iomem *regs; + struct clk *clk_sense; + struct clk *clk_bus; + struct reset_control *rst_sense; + struct reset_control *rst_bus; + bool enabled; +}; + +static irqreturn_t sfctemp_isr(int irq, void *data) +{ + struct sfctemp *sfctemp = data; + + complete(&sfctemp->conversion_done); + return IRQ_HANDLED; +} + +static void sfctemp_power_up(struct sfctemp *sfctemp) +{ + /* make sure we're powered down first */ + writel(SFCTEMP_PD, sfctemp->regs); + udelay(1); + + writel(0, sfctemp->regs); + /* wait t_pu(50us) + t_rst(100ns) */ + usleep_range(60, 200); + + /* de-assert reset */ + writel(SFCTEMP_RSTN, sfctemp->regs); + udelay(1); /* wait t_su(500ps) */ +} + +static void sfctemp_power_down(struct sfctemp *sfctemp) +{ + writel(SFCTEMP_PD, sfctemp->regs); +} + +static void sfctemp_run_single(struct sfctemp *sfctemp) +{ + writel(SFCTEMP_RSTN | SFCTEMP_RUN, sfctemp->regs); + udelay(1); + writel(SFCTEMP_RSTN, sfctemp->regs); +} + +static int sfctemp_enable(struct sfctemp *sfctemp) +{ + int ret = 0; + + mutex_lock(&sfctemp->lock); + if (sfctemp->enabled) + goto done; + + ret = clk_prepare_enable(sfctemp->clk_bus); + if (ret) + goto err; + ret = reset_control_deassert(sfctemp->rst_bus); + if (ret) + goto err_disable_bus; + + ret = clk_prepare_enable(sfctemp->clk_sense); + if (ret) + goto err_assert_bus; + ret = reset_control_deassert(sfctemp->rst_sense); + if (ret) + goto err_disable_sense; + + sfctemp_power_up(sfctemp); + sfctemp->enabled = true; +done: + mutex_unlock(&sfctemp->lock); + return ret; + +err_disable_sense: + clk_disable_unprepare(sfctemp->clk_sense); +err_assert_bus: + reset_control_assert(sfctemp->rst_bus); +err_disable_bus: + clk_disable_unprepare(sfctemp->clk_bus); +err: + mutex_unlock(&sfctemp->lock); + return ret; +} + +static int sfctemp_disable(struct sfctemp *sfctemp) +{ + mutex_lock(&sfctemp->lock); + if (!sfctemp->enabled) + goto done; + + sfctemp_power_down(sfctemp); + reset_control_assert(sfctemp->rst_sense); + clk_disable_unprepare(sfctemp->clk_sense); + reset_control_assert(sfctemp->rst_bus); + clk_disable_unprepare(sfctemp->clk_bus); + sfctemp->enabled = false; +done: + mutex_unlock(&sfctemp->lock); + return 0; +} + +static void sfctemp_disable_action(void *data) +{ + sfctemp_disable(data); +} + +static int sfctemp_convert(struct sfctemp *sfctemp, long *val) +{ + int ret; + + mutex_lock(&sfctemp->lock); + if (!sfctemp->enabled) { + ret = -ENODATA; + goto out; + } + + sfctemp_run_single(sfctemp); + + ret = wait_for_completion_interruptible_timeout(&sfctemp->conversion_done, + msecs_to_jiffies(10)); + if (ret <= 0) { + if (ret == 0) + ret = -ETIMEDOUT; + goto out; + } + + /* calculate temperature in milli Celcius */ + *val = (long)((readl(sfctemp->regs) & SFCTEMP_DOUT_MSK) >> SFCTEMP_DOUT_POS) + * SFCTEMP_Y1000 / SFCTEMP_Z - SFCTEMP_K1000; + + ret = 0; +out: + mutex_unlock(&sfctemp->lock); + return ret; +} + +static umode_t sfctemp_is_visible(const void *data, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (type) { + case hwmon_temp: + switch (attr) { + case hwmon_temp_enable: + return 0644; + case hwmon_temp_input: + return 0444; + } + return 0; + default: + return 0; + } +} + +static int sfctemp_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct sfctemp *sfctemp = dev_get_drvdata(dev); + + switch (type) { + case hwmon_temp: + switch (attr) { + case hwmon_temp_enable: + *val = sfctemp->enabled; + return 0; + case hwmon_temp_input: + return sfctemp_convert(sfctemp, val); + } + return -EINVAL; + default: + return -EINVAL; + } +} + +static int sfctemp_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + struct sfctemp *sfctemp = dev_get_drvdata(dev); + + switch (type) { + case hwmon_temp: + switch (attr) { + case hwmon_temp_enable: + if (val == 0) + return sfctemp_disable(sfctemp); + if (val == 1) + return sfctemp_enable(sfctemp); + break; + } + return -EINVAL; + default: + return -EINVAL; + } +} + +static const struct hwmon_channel_info *sfctemp_info[] = { + HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ), + HWMON_CHANNEL_INFO(temp, HWMON_T_ENABLE | HWMON_T_INPUT), + NULL +}; + +static const struct hwmon_ops sfctemp_hwmon_ops = { + .is_visible = sfctemp_is_visible, + .read = sfctemp_read, + .write = sfctemp_write, +}; + +static const struct hwmon_chip_info sfctemp_chip_info = { + .ops = &sfctemp_hwmon_ops, + .info = sfctemp_info, +}; + +static int sfctemp_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device *hwmon_dev; + struct sfctemp *sfctemp; + int ret; + + sfctemp = devm_kzalloc(dev, sizeof(*sfctemp), GFP_KERNEL); + if (!sfctemp) + return -ENOMEM; + + dev_set_drvdata(dev, sfctemp); + mutex_init(&sfctemp->lock); + init_completion(&sfctemp->conversion_done); + + sfctemp->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(sfctemp->regs)) + return PTR_ERR(sfctemp->regs); + + sfctemp->clk_sense = devm_clk_get(dev, "sense"); + if (IS_ERR(sfctemp->clk_sense)) + return dev_err_probe(dev, PTR_ERR(sfctemp->clk_sense), + "error getting sense clock\n"); + + sfctemp->clk_bus = devm_clk_get(dev, "bus"); + if (IS_ERR(sfctemp->clk_bus)) + return dev_err_probe(dev, PTR_ERR(sfctemp->clk_bus), + "error getting bus clock\n"); + + sfctemp->rst_sense = devm_reset_control_get_exclusive(dev, "sense"); + if (IS_ERR(sfctemp->rst_sense)) + return dev_err_probe(dev, PTR_ERR(sfctemp->rst_sense), + "error getting sense reset\n"); + + sfctemp->rst_bus = devm_reset_control_get_exclusive(dev, "bus"); + if (IS_ERR(sfctemp->rst_bus)) + return dev_err_probe(dev, PTR_ERR(sfctemp->rst_bus), + "error getting busreset\n"); + + ret = reset_control_assert(sfctemp->rst_sense); + if (ret) + return dev_err_probe(dev, ret, "error asserting sense reset\n"); + + ret = reset_control_assert(sfctemp->rst_bus); + if (ret) + return dev_err_probe(dev, ret, "error asserting bus reset\n"); + + ret = platform_get_irq(pdev, 0); + if (ret < 0) + return ret; + + ret = devm_request_irq(dev, ret, sfctemp_isr, 0, pdev->name, sfctemp); + if (ret) + return dev_err_probe(dev, ret, "error requesting irq\n"); + + ret = devm_add_action(dev, sfctemp_disable_action, sfctemp); + if (ret) + return ret; + + ret = sfctemp_enable(sfctemp); + if (ret) + return dev_err_probe(dev, ret, "error enabling temperature sensor: %d\n", ret); + + hwmon_dev = devm_hwmon_device_register_with_info(dev, pdev->name, sfctemp, + &sfctemp_chip_info, NULL); + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct of_device_id sfctemp_of_match[] = { + { .compatible = "starfive,jh7100-temp" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, sfctemp_of_match); + +static struct platform_driver sfctemp_driver = { + .probe = sfctemp_probe, + .driver = { + .name = "sfctemp", + .of_match_table = sfctemp_of_match, + }, +}; +module_platform_driver(sfctemp_driver); + +MODULE_AUTHOR("Emil Renner Berthing"); +MODULE_DESCRIPTION("StarFive JH7100 temperature sensor driver"); +MODULE_LICENSE("GPL"); From d982badbcef3764ca3599892ee671889ac27bcba Mon Sep 17 00:00:00 2001 From: Samin Guo Date: Wed, 17 Nov 2021 11:06:25 +0800 Subject: [PATCH 44/80] watchdog: Add StarFive SI5 watchdog driver Signed-off-by: Samin Guo Signed-off-by: Walker Chen --- drivers/watchdog/Kconfig | 9 + drivers/watchdog/Makefile | 3 + drivers/watchdog/starfive-wdt.c | 776 ++++++++++++++++++++++++++++++++ 3 files changed, 788 insertions(+) create mode 100644 drivers/watchdog/starfive-wdt.c diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 9d222ba17ec60..69fb8a041dc2c 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -1995,6 +1995,15 @@ config UML_WATCHDOG tristate "UML watchdog" depends on UML || COMPILE_TEST +# RISCV Architecture + +config STARFIVE_WATCHDOG + tristate "StarFive Watchdog support" + depends on RISCV + select WATCHDOG_CORE + help + Say Y here to support the starfive Si5 watchdog. + # # ISA-based Watchdog Cards # diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 2ee97064145bf..576817b34d29a 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -202,6 +202,9 @@ obj-$(CONFIG_WATCHDOG_SUN4V) += sun4v_wdt.o # Xen obj-$(CONFIG_XEN_WDT) += xen_wdt.o +# RISCV Architecture +obj-$(CONFIG_STARFIVE_WATCHDOG) += starfive-wdt.o + # Architecture Independent obj-$(CONFIG_BD957XMUF_WATCHDOG) += bd9576_wdt.o obj-$(CONFIG_DA9052_WATCHDOG) += da9052_wdt.o diff --git a/drivers/watchdog/starfive-wdt.c b/drivers/watchdog/starfive-wdt.c new file mode 100644 index 0000000000000..68fb872694abe --- /dev/null +++ b/drivers/watchdog/starfive-wdt.c @@ -0,0 +1,776 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Watchdog driver for the StarFive JH7100 SoC + * + * Copyright (C) 2021 Samin Guo + * Copyright (C) 2021 Walker Chen + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* JH7100 WatchDog register define */ +#define JH7100_WDGINTSTAUS 0x000 +#define JH7100_WDOGCONTROL 0x104 /* Watchdog Control Register R/W */ +#define JH7100_WDOGLOAD 0x108 /* The initial value to be loaded */ + /* into the counter and is also used */ + /* as the reload value. R/W */ +#define JH7100_WDOGEN 0x110 /* Watchdog enable Register */ +#define JH7100_WDOGRELOAD 0x114 /* Write this register to reload preset */ + /* value to counter. (Write 0 or 1 are both ok) */ +#define JH7100_WDOGVALUE 0x118 /* Watchdog Value Register RO */ +#define JH7100_WDOGINTCLR 0x120 /* Watchdog Clear Interrupt Register WO */ +#define JH7100_WDOGINTMSK 0x124 /* Watchdog Interrupt Mask Register */ +#define JH7100_WDOGLOCK 0x13c /* Watchdog Lock Register R/W */ + +#define JH7100_UNLOCK_KEY 0x378f0765 +#define JH7100_RESEN_SHIFT 0 +#define JH7100_EN_SHIFT 0 +#define JH7100_INTCLR_AVA_SHIFT 1 /* Watchdog can clear interrupt when this bit is 0 */ + +/* WDOGCONTROL */ +#define WDOG_INT_EN 0x0 +#define WDOG_RESET_EN 0x1 + +/* WDOGLOCK */ +#define WDOG_LOCKED BIT(0) + +#define SI5_WATCHDOG_INTCLR 0x1 +#define SI5_WATCHDOG_ENABLE 0x1 +#define SI5_WATCHDOG_ATBOOT 0x0 +#define SI5_WATCHDOG_MAXCNT 0xffffffff + +#define SI5_WATCHDOG_DEFAULT_TIME (15) + +static bool nowayout = WATCHDOG_NOWAYOUT; +static int tmr_margin; +static int tmr_atboot = SI5_WATCHDOG_ATBOOT; +static int soft_noboot; + +module_param(tmr_margin, int, 0); +module_param(tmr_atboot, int, 0); +module_param(nowayout, bool, 0); +module_param(soft_noboot, int, 0); + +MODULE_PARM_DESC(tmr_margin, "Watchdog tmr_margin in seconds. (default=" + __MODULE_STRING(SI5_WATCHDOG_DEFAULT_TIME) ")"); +MODULE_PARM_DESC(tmr_atboot, + "Watchdog is started at boot time if set to 1, default=" + __MODULE_STRING(SI5_WATCHDOG_ATBOOT)); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); +MODULE_PARM_DESC(soft_noboot, "Watchdog action, set to 1 to ignore reboots, 0 to reboot (default 0)"); + +struct si5_wdt_variant_t { + u32 unlock_key; + u8 enrst_shift; + u8 en_shift; + u8 intclr_check; + u8 intclr_ava_shift; +}; + +struct si5_wdt_variant { + u32 control; + u32 load; + u32 enable; + u32 reload; + u32 value; + u32 int_clr; + u32 int_mask; + u32 unlock; + struct si5_wdt_variant_t *variant; +}; + +struct stf_si5_wdt { + u64 freq; + struct device *dev; + struct watchdog_device wdt_device; + struct clk *core_clk; + struct clk *apb_clk; + struct reset_control *rst_wdtimer_apb; + struct reset_control *rst_wdt; + + const struct si5_wdt_variant *drv_data; + u32 count; /*count of timeout*/ + u32 reload; /*restore the count*/ + void __iomem *base; + spinlock_t lock; +}; + +#ifdef CONFIG_OF +static struct si5_wdt_variant_t jh7100_variant = { + .unlock_key = JH7100_UNLOCK_KEY, + .enrst_shift = JH7100_RESEN_SHIFT, + .en_shift = JH7100_EN_SHIFT, + .intclr_check = 1, + .intclr_ava_shift = JH7100_INTCLR_AVA_SHIFT, +}; + +static const struct si5_wdt_variant drv_data_jh7100 = { + .control = JH7100_WDOGCONTROL, + .load = JH7100_WDOGLOAD, + .enable = JH7100_WDOGEN, + .reload = JH7100_WDOGRELOAD, + .value = JH7100_WDOGVALUE, + .int_clr = JH7100_WDOGINTCLR, + .int_mask = JH7100_WDOGINTMSK, + .unlock = JH7100_WDOGLOCK, + .variant = &jh7100_variant, +}; + +static const struct of_device_id starfive_wdt_match[] = { + { .compatible = "starfive,si5-wdt", + .data = &drv_data_jh7100 }, + {}, +}; +MODULE_DEVICE_TABLE(of, starfive_wdt_match); +#endif + +static const struct platform_device_id si5wdt_ids[] = { + { + .name = "starfive-si5-wdt", + .driver_data = (unsigned long)&drv_data_jh7100, + }, + {} +}; +MODULE_DEVICE_TABLE(platform, si5wdt_ids); + +static int si5wdt_get_clock_rate(struct stf_si5_wdt *wdt) +{ + int ret; + u32 freq; + + if (!IS_ERR(wdt->core_clk)) { + wdt->freq = clk_get_rate(wdt->core_clk); + return 0; + } + + /* Next we try to get clock-frequency from dts.*/ + ret = of_property_read_u32(wdt->dev->of_node, "clock-frequency", &freq); + if (!ret) { + wdt->freq = (u64)freq; + return 0; + } + else + dev_err(wdt->dev, "get rate failed, need clock-frequency define in dts.\n"); + + return -ENOENT; +} + +static int si5wdt_enable_clock(struct stf_si5_wdt *wdt) +{ + struct reset_control_bulk_data resets[] = { + { .id = "wdtimer_apb" }, + { .id = "wdt" }, + }; + int ret; + + ret = devm_reset_control_bulk_get_exclusive(wdt->dev, ARRAY_SIZE(resets), resets); + if (ret) { + printk(KERN_INFO "faied to get watchdog reset controls\n"); + return ret; + } + + wdt->rst_wdtimer_apb = resets[0].rstc; + wdt->rst_wdt = resets[1].rstc; + + wdt->apb_clk = devm_clk_get(wdt->dev, "wdtimer_apb"); + if (!IS_ERR(wdt->apb_clk)) { + ret = clk_prepare_enable(wdt->apb_clk); + if(ret) + dev_warn(wdt->dev, "enable core_clk error.\n"); + } + + wdt->core_clk = devm_clk_get(wdt->dev, "wdt_coreclk"); + if (!IS_ERR(wdt->core_clk)) { + ret = clk_prepare_enable(wdt->core_clk); + if(ret) + dev_warn(wdt->dev, "enable apb_clk error.\n"); + } + + ret = reset_control_assert(wdt->rst_wdtimer_apb); + if (ret) { + printk(KERN_INFO "failed to assert wdtimer_apb\n"); + return ret; + } + + ret = reset_control_assert(wdt->rst_wdt); + if (ret) { + printk(KERN_INFO "failed to assert wdt\n"); + return ret; + } + + ret = reset_control_deassert(wdt->rst_wdtimer_apb); + if (ret) { + printk(KERN_INFO "failed to deassert wdtimer_apb, ret=%d\n", ret); + return ret; + } + + ret = reset_control_deassert(wdt->rst_wdt); + if (ret) { + printk(KERN_INFO "failed to deassert wdt\n"); + return ret; + } + + return 0; +} + +static __maybe_unused +u32 si5wdt_sec_to_ticks(struct stf_si5_wdt *wdt, u32 sec) +{ + return sec * wdt->freq; +} + +static __maybe_unused +u32 si5wdt_ticks_to_sec(struct stf_si5_wdt *wdt, u32 ticks) +{ + return DIV_ROUND_CLOSEST(ticks, wdt->freq); +} + +/* + * Write unlock-key to unlock. Write other value to lock. When lock bit is 1, + * external accesses to other watchdog registers are ignored. + */ +static int si5wdt_is_locked(struct stf_si5_wdt *wdt) +{ + u32 val; + + val = readl(wdt->base + wdt->drv_data->unlock); + return !!(val & WDOG_LOCKED); +} + +static void si5wdt_unlock(struct stf_si5_wdt *wdt) +{ + if (si5wdt_is_locked(wdt)) + writel(wdt->drv_data->variant->unlock_key, + wdt->base + wdt->drv_data->unlock); +} + +static void si5wdt_lock(struct stf_si5_wdt *wdt) +{ + if (!si5wdt_is_locked(wdt)) + writel(~wdt->drv_data->variant->unlock_key, + wdt->base + wdt->drv_data->unlock); +} + +static int __maybe_unused si5wdt_is_running(struct stf_si5_wdt *wdt) +{ + u32 val; + + si5wdt_unlock(wdt); + val = readl(wdt->base + wdt->drv_data->enable); + si5wdt_lock(wdt); + + return !!(val & SI5_WATCHDOG_ENABLE << + wdt->drv_data->variant->en_shift); +} + +static inline void si5wdt_int_enable(struct stf_si5_wdt *wdt) +{ + u32 val; + + if (wdt->drv_data->int_mask) { + val = readl(wdt->base + wdt->drv_data->int_mask); + val &= ~(1<<0); + writel(val, wdt->base + wdt->drv_data->int_mask); + } +} + +static inline void si5wdt_int_disable(struct stf_si5_wdt *wdt) +{ + u32 val; + + if (wdt->drv_data->int_mask) { + val = readl(wdt->base + wdt->drv_data->int_mask); + val |= (1<<0); + writel(val, wdt->base + wdt->drv_data->int_mask); + } +} + +static void si5wdt_enable_reset(struct stf_si5_wdt *wdt) +{ + u32 val; + + val = readl(wdt->base + wdt->drv_data->control); + val |= WDOG_RESET_EN << wdt->drv_data->variant->enrst_shift; + /* enable wdog interrupt to reset */ + writel(val, wdt->base + wdt->drv_data->control); +} + +static void si5wdt_disable_reset(struct stf_si5_wdt *wdt) +{ + u32 val; + + val = readl(wdt->base + wdt->drv_data->control); + val &= ~(WDOG_RESET_EN << wdt->drv_data->variant->enrst_shift); + /*disable wdog interrupt to reset*/ + writel(val, wdt->base + wdt->drv_data->control); +} + +static void si5wdt_int_clr(struct stf_si5_wdt *wdt) +{ + void __iomem *addr; + u8 clr_check; + u8 clr_ava_shift; + + addr = wdt->base + wdt->drv_data->int_clr; + clr_ava_shift = wdt->drv_data->variant->intclr_ava_shift; + clr_check = wdt->drv_data->variant->intclr_check; + if (clr_check) { + /* waiting interrupt can be to clearing */ + do { + + } while (readl(addr) & BIT(clr_ava_shift)); + } + + writel(SI5_WATCHDOG_INTCLR, addr); +} + +static inline void si5wdt_set_count(struct stf_si5_wdt *wdt, u32 val) +{ + writel(val, wdt->base + wdt->drv_data->load); +} + +static inline u32 si5wdt_get_count(struct stf_si5_wdt *wdt) +{ + return readl(wdt->base + wdt->drv_data->value); +} + +static inline void si5wdt_enable(struct stf_si5_wdt *wdt) +{ + u32 val; + + val = readl(wdt->base + wdt->drv_data->enable); + val |= SI5_WATCHDOG_ENABLE << wdt->drv_data->variant->en_shift; + writel(val, wdt->base + wdt->drv_data->enable); +} + +static inline void si5wdt_disable(struct stf_si5_wdt *wdt) +{ + u32 val; + + val = readl(wdt->base + wdt->drv_data->enable); + val &= ~(SI5_WATCHDOG_ENABLE << wdt->drv_data->variant->en_shift); + writel(val, wdt->base + wdt->drv_data->enable); +} + +static inline void +si5wdt_set_relod_count(struct stf_si5_wdt *wdt, u32 count) +{ + writel(count, wdt->base + wdt->drv_data->load); + if (wdt->drv_data->reload) + writel(0x1, wdt->base + wdt->drv_data->reload); + +} + +static int si5wdt_mask_and_disable_reset(struct stf_si5_wdt *wdt, bool mask) +{ + si5wdt_unlock(wdt); + + if (mask) + si5wdt_disable_reset(wdt); + else + si5wdt_enable_reset(wdt); + + si5wdt_lock(wdt); + + return 0; +} + +static unsigned int si5wdt_max_timeout(struct stf_si5_wdt *wdt) +{ + return DIV_ROUND_UP(SI5_WATCHDOG_MAXCNT, wdt->freq) - 1; +} + +static unsigned int si5wdt_get_timeleft(struct watchdog_device *wdd) +{ + struct stf_si5_wdt *wdt = watchdog_get_drvdata(wdd); + u32 count; + + si5wdt_unlock(wdt); + count = si5wdt_get_count(wdt); + si5wdt_lock(wdt); + + return si5wdt_ticks_to_sec(wdt, count); +} + +static int si5wdt_keepalive(struct watchdog_device *wdd) +{ + struct stf_si5_wdt *wdt = watchdog_get_drvdata(wdd); + + spin_lock(&wdt->lock); + + si5wdt_unlock(wdt); + si5wdt_set_relod_count(wdt, wdt->count); + si5wdt_lock(wdt); + + spin_unlock(&wdt->lock); + + return 0; +} + +static irqreturn_t si5wdt_interrupt_handler(int irq, void *data) +{ + /* + * We don't clear the IRQ status. It's supposed to be done by the + * following ping operations. + */ + + return IRQ_HANDLED; +} + +static int si5wdt_stop(struct watchdog_device *wdd) +{ + struct stf_si5_wdt *wdt = watchdog_get_drvdata(wdd); + + spin_lock(&wdt->lock); + + si5wdt_unlock(wdt); + si5wdt_int_disable(wdt); + si5wdt_int_clr(wdt); + si5wdt_disable(wdt); + si5wdt_lock(wdt); + + spin_unlock(&wdt->lock); + + return 0; +} + +static int si5wdt_start(struct watchdog_device *wdd) +{ + struct stf_si5_wdt *wdt = watchdog_get_drvdata(wdd); + + spin_lock(&wdt->lock); + + si5wdt_unlock(wdt); + + if (soft_noboot) + si5wdt_disable_reset(wdt); + else + si5wdt_enable_reset(wdt); + + si5wdt_set_count(wdt, wdt->count); + si5wdt_int_enable(wdt); + si5wdt_enable(wdt); + + si5wdt_lock(wdt); + + spin_unlock(&wdt->lock); + + return 0; +} + +static int si5wdt_restart(struct watchdog_device *wdd, unsigned long action, + void *data) +{ + struct stf_si5_wdt *wdt = watchdog_get_drvdata(wdd); + + si5wdt_unlock(wdt); + /* disable watchdog, to be safe */ + si5wdt_disable(wdt); + + if (soft_noboot) + si5wdt_disable_reset(wdt); + else + si5wdt_enable_reset(wdt); + + /* put initial values into count and data */ + si5wdt_set_count(wdt, wdt->count); + + /* set the watchdog to go and reset... */ + si5wdt_int_clr(wdt); + si5wdt_int_enable(wdt); + si5wdt_enable(wdt); + + /* wait for reset to assert... */ + mdelay(500); + + si5wdt_lock(wdt); + + return 0; +} + +static int si5wdt_set_timeout(struct watchdog_device *wdd, + unsigned int timeout) +{ + struct stf_si5_wdt *wdt = watchdog_get_drvdata(wdd); + + unsigned long freq = wdt->freq; + unsigned int count; + + if (timeout < 1) + return -EINVAL; + + count = timeout * freq; + + if (count > SI5_WATCHDOG_MAXCNT) { + dev_warn(wdt->dev, "timeout %d too big,use the MAX-timeout set.\n", + timeout); + timeout = si5wdt_max_timeout(wdt); + count = timeout * freq; + } + + dev_info(wdt->dev, "Heartbeat: timeout=%d, count=%d (%08x)\n", + timeout, count, count); + + si5wdt_unlock(wdt); + si5wdt_disable(wdt); + si5wdt_set_relod_count(wdt, count); + si5wdt_enable(wdt); + si5wdt_lock(wdt); + + wdt->count = count; + wdd->timeout = timeout; + + return 0; +} + +#define OPTIONS (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE) + +static const struct watchdog_info si5_wdt_ident = { + .options = OPTIONS, + .firmware_version = 0, + .identity = "StarFive SI5 Watchdog", +}; + +static const struct watchdog_ops si5wdt_ops = { + .owner = THIS_MODULE, + .start = si5wdt_start, + .stop = si5wdt_stop, + .ping = si5wdt_keepalive, + .set_timeout = si5wdt_set_timeout, + .restart = si5wdt_restart, + .get_timeleft = si5wdt_get_timeleft, +}; + +static const struct watchdog_device starfive_si5_wdd = { + .info = &si5_wdt_ident, + .ops = &si5wdt_ops, + .timeout = SI5_WATCHDOG_DEFAULT_TIME, +}; + +static inline const struct si5_wdt_variant * +si5_get_wdt_drv_data(struct platform_device *pdev) +{ + const struct si5_wdt_variant *variant; + + variant = of_device_get_match_data(&pdev->dev); + if (!variant) { + /* Device matched by platform_device_id */ + variant = (struct si5_wdt_variant *) + platform_get_device_id(pdev)->driver_data; + } + + return variant; +} + +static int si5wdt_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct stf_si5_wdt *wdt; + struct resource *wdt_irq; + int started = 0; + int ret; + + wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL); + if (!wdt) + return -ENOMEM; + + wdt->dev = dev; + spin_lock_init(&wdt->lock); + wdt->wdt_device = starfive_si5_wdd; + + wdt->drv_data = si5_get_wdt_drv_data(pdev); + + wdt_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (wdt_irq == NULL) { + dev_err(dev, "no irq resource specified\n"); + ret = -ENOENT; + goto err; + } + + /* get the memory region for the watchdog timer */ + wdt->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(wdt->base)) { + ret = PTR_ERR(wdt->base); + goto err; + } + + ret = si5wdt_enable_clock(wdt); + if (ret) + dev_warn(wdt->dev, "get & enable clk err\n"); + + si5wdt_get_clock_rate(wdt); + + wdt->wdt_device.min_timeout = 1; + wdt->wdt_device.max_timeout = si5wdt_max_timeout(wdt); + + watchdog_set_drvdata(&wdt->wdt_device, wdt); + + /* + * see if we can actually set the requested timer margin, + * and if not, try the default value. + */ + watchdog_init_timeout(&wdt->wdt_device, tmr_margin, dev); + + ret = si5wdt_set_timeout(&wdt->wdt_device, + wdt->wdt_device.timeout); + if (ret) { + dev_info(dev, "tmr_margin value out of range, default %d used\n", + SI5_WATCHDOG_DEFAULT_TIME); + si5wdt_set_timeout(&wdt->wdt_device, + SI5_WATCHDOG_DEFAULT_TIME); + } + + ret = devm_request_irq(dev, wdt_irq->start, si5wdt_interrupt_handler, 0, + pdev->name, pdev); + if (ret != 0) { + dev_err(dev, "failed to install irq (%d)\n", ret); + goto err; + } + + watchdog_set_nowayout(&wdt->wdt_device, nowayout); + watchdog_set_restart_priority(&wdt->wdt_device, 128); + + wdt->wdt_device.parent = dev; + + ret = watchdog_register_device(&wdt->wdt_device); + if (ret) + goto err; + + ret = si5wdt_mask_and_disable_reset(wdt, false); + if (ret < 0) + goto err_unregister; + + if (tmr_atboot && started == 0) { + dev_info(dev, "starting watchdog timer\n"); + si5wdt_start(&wdt->wdt_device); + } else if (!tmr_atboot) { + + /* + *if we're not enabling the watchdog, then ensure it is + * disabled if it has been left running from the bootloader + * or other source. + */ + si5wdt_stop(&wdt->wdt_device); + } + + platform_set_drvdata(pdev, wdt); + + return 0; + + err_unregister: + watchdog_unregister_device(&wdt->wdt_device); + + err: + return ret; +} + +static int si5wdt_remove(struct platform_device *dev) +{ + int ret; + struct stf_si5_wdt *wdt = platform_get_drvdata(dev); + + ret = si5wdt_mask_and_disable_reset(wdt, true); + if (ret < 0) + return ret; + + watchdog_unregister_device(&wdt->wdt_device); + + clk_disable_unprepare(wdt->core_clk); + + return 0; +} + +static void si5wdt_shutdown(struct platform_device *dev) +{ + struct stf_si5_wdt *wdt = platform_get_drvdata(dev); + + si5wdt_mask_and_disable_reset(wdt, true); + + si5wdt_stop(&wdt->wdt_device); + +} + +#ifdef CONFIG_PM_SLEEP + +static int si5wdt_suspend(struct device *dev) +{ + int ret; + struct stf_si5_wdt *wdt = dev_get_drvdata(dev); + + si5wdt_unlock(wdt); + + /* Save watchdog state, and turn it off. */ + wdt->reload = si5wdt_get_count(wdt); + + ret = si5wdt_mask_and_disable_reset(wdt, true); + if (ret < 0) + return ret; + + /* Note that WTCNT doesn't need to be saved. */ + si5wdt_stop(&wdt->wdt_device); + + si5wdt_lock(wdt); + + return 0; +} + +static int si5wdt_resume(struct device *dev) +{ + int ret; + struct stf_si5_wdt *wdt = dev_get_drvdata(dev); + + si5wdt_unlock(wdt); + + /* Restore watchdog state. */ + si5wdt_set_relod_count(wdt, wdt->reload); + + ret = si5wdt_mask_and_disable_reset(wdt, false); + if (ret < 0) + return ret; + + si5wdt_lock(wdt); + + dev_info(dev, "watchdog resume\n") + + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +static SIMPLE_DEV_PM_OPS(si5wdt_pm_ops, si5wdt_suspend, + si5wdt_resume); + +static struct platform_driver starfive_si5wdt_driver = { + .probe = si5wdt_probe, + .remove = si5wdt_remove, + .shutdown = si5wdt_shutdown, + .id_table = si5wdt_ids, + .driver = { + .name = "starfive-si5-wdt", + .pm = &si5wdt_pm_ops, + .of_match_table = of_match_ptr(starfive_wdt_match), + }, +}; + +module_platform_driver(starfive_si5wdt_driver); + +MODULE_AUTHOR("samin.guo "); +MODULE_AUTHOR("Walker Chen "); +MODULE_DESCRIPTION("StarFive SI5 Watchdog Device Driver"); +MODULE_LICENSE("GPL v2"); From a72e326a185dd28d7c0d2416003df28fdb31a8c5 Mon Sep 17 00:00:00 2001 From: Huan Feng Date: Fri, 8 Jan 2021 03:35:42 +0800 Subject: [PATCH 45/80] drivers/hw_random: Add StarFive JH7100 Random Number Generator driver --- drivers/char/hw_random/Kconfig | 13 ++ drivers/char/hw_random/Makefile | 1 + drivers/char/hw_random/starfive-vic-rng.c | 256 ++++++++++++++++++++++ drivers/char/hw_random/starfive-vic-rng.h | 167 ++++++++++++++ 4 files changed, 437 insertions(+) create mode 100644 drivers/char/hw_random/starfive-vic-rng.c create mode 100644 drivers/char/hw_random/starfive-vic-rng.h diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig index 814b3d0ca7b77..105adc266f6d1 100644 --- a/drivers/char/hw_random/Kconfig +++ b/drivers/char/hw_random/Kconfig @@ -335,6 +335,19 @@ config HW_RANDOM_POWERNV If unsure, say Y. +config HW_RANDOM_STARFIVE_VIC + tristate "Starfive VIC Random Number Generator support" + depends on HW_RANDOM && (SOC_STARFIVE || COMPILE_TEST) + default SOC_STARFIVE + help + This driver provides kernel-side support for the Random Number + Generator hardware found on Starfive VIC SoC. + + To compile this driver as a module, choose M here: the + module will be called starfive-vic-rng. + + If unsure, say Y. + config HW_RANDOM_HISI tristate "Hisilicon Random Number Generator support" depends on HW_RANDOM && ARCH_HISI diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile index a5a1c765a3946..412f9a0b2a6d4 100644 --- a/drivers/char/hw_random/Makefile +++ b/drivers/char/hw_random/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_HW_RANDOM_OCTEON) += octeon-rng.o obj-$(CONFIG_HW_RANDOM_NOMADIK) += nomadik-rng.o obj-$(CONFIG_HW_RANDOM_PSERIES) += pseries-rng.o obj-$(CONFIG_HW_RANDOM_POWERNV) += powernv-rng.o +obj-$(CONFIG_HW_RANDOM_STARFIVE_VIC) += starfive-vic-rng.o obj-$(CONFIG_HW_RANDOM_HISI) += hisi-rng.o obj-$(CONFIG_HW_RANDOM_BCM2835) += bcm2835-rng.o obj-$(CONFIG_HW_RANDOM_IPROC_RNG200) += iproc-rng200.o diff --git a/drivers/char/hw_random/starfive-vic-rng.c b/drivers/char/hw_random/starfive-vic-rng.c new file mode 100644 index 0000000000000..6142b6a7ace6b --- /dev/null +++ b/drivers/char/hw_random/starfive-vic-rng.c @@ -0,0 +1,256 @@ +/* + ****************************************************************************** + * @file starfive-vic-rng.c + * @author StarFive Technology + * @version V1.0 + * @date 08/13/2020 + * @brief + ****************************************************************************** + * @copy + * + * THE PRESENT SOFTWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STARFIVE SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH SOFTWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + * COPYRIGHT 2020 Shanghai StarFive Technology Co., Ltd. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "starfive-vic-rng.h" + +#define to_vic_rng(p) container_of(p, struct vic_rng, rng) + +struct vic_rng { + struct device *dev; + void __iomem *base; + struct hwrng rng; +}; + +static inline void vic_wait_till_idle(struct vic_rng *hrng) +{ + while(readl(hrng->base + VIC_STAT) & VIC_STAT_BUSY) + ; +} + +static inline void vic_rng_irq_mask_clear(struct vic_rng *hrng) +{ + // clear register: ISTAT + u32 data = readl(hrng->base + VIC_ISTAT); + writel(data, hrng->base + VIC_ISTAT); + writel(0, hrng->base + VIC_ALARM); +} + +static int vic_trng_cmd(struct vic_rng *hrng, u32 cmd) { + int res = 0; + // wait till idle + vic_wait_till_idle(hrng); + switch (cmd) { + case VIC_CTRL_CMD_NOP: + case VIC_CTRL_CMD_GEN_NOISE: + case VIC_CTRL_CMD_GEN_NONCE: + case VIC_CTRL_CMD_CREATE_STATE: + case VIC_CTRL_CMD_RENEW_STATE: + case VIC_CTRL_CMD_REFRESH_ADDIN: + case VIC_CTRL_CMD_GEN_RANDOM: + case VIC_CTRL_CMD_ADVANCE_STATE: + case VIC_CTRL_CMD_KAT: + case VIC_CTRL_CMD_ZEROIZE: + writel(cmd, hrng->base + VIC_CTRL); + break; + default: + res = -1; + break; + } + + return res; +} + +static int vic_rng_init(struct hwrng *rng) +{ + struct vic_rng *hrng = to_vic_rng(rng); + + // wait till idle + + // clear register: ISTAT + vic_rng_irq_mask_clear(hrng); + + // set mission mode + writel(VIC_SMODE_SECURE_EN(1), hrng->base + VIC_SMODE); + + vic_trng_cmd(hrng, VIC_CTRL_CMD_GEN_NOISE); + vic_wait_till_idle(hrng); + + // set interrupt + writel(VIC_IE_ALL, hrng->base + VIC_IE); + + // zeroize + vic_trng_cmd(hrng, VIC_CTRL_CMD_ZEROIZE); + + vic_wait_till_idle(hrng); + + return 0; +} + +static irqreturn_t vic_rng_irq(int irq, void *priv) +{ + u32 status, val; + struct vic_rng *hrng = (struct vic_rng *)priv; + + /* + * clearing the interrupt will also clear the error register + * read error and status before clearing + */ + status = readl(hrng->base + VIC_ISTAT); + + if (status & VIC_ISTAT_ALARMS) { + writel(VIC_ISTAT_ALARMS, hrng->base + VIC_ISTAT); + val = readl(hrng->base + VIC_ALARM); + if (val & VIC_ALARM_ILLEGAL_CMD_SEQ) { + writel(VIC_ALARM_ILLEGAL_CMD_SEQ, hrng->base + VIC_ALARM); + //dev_info(hrng->dev, "ILLEGAL CMD SEQ: LAST_CMD=0x%x\r\n", + //VIC_STAT_LAST_CMD(readl(hrng->base + VIC_STAT))); + } else { + dev_info(hrng->dev, "Failed test: %x\r\n", val); + } + } + + if (status & VIC_ISTAT_ZEROIZE) { + writel(VIC_ISTAT_ZEROIZE, hrng->base + VIC_ISTAT); + //dev_info(hrng->dev, "zeroized\r\n"); + } + + if (status & VIC_ISTAT_KAT_COMPLETE) { + writel(VIC_ISTAT_KAT_COMPLETE, hrng->base + VIC_ISTAT); + //dev_info(hrng->dev, "kat_completed\r\n"); + } + + if (status & VIC_ISTAT_NOISE_RDY) { + writel(VIC_ISTAT_NOISE_RDY, hrng->base + VIC_ISTAT); + //dev_info(hrng->dev, "noise_rdy\r\n"); + } + + if (status & VIC_ISTAT_DONE) { + writel(VIC_ISTAT_DONE, hrng->base + VIC_ISTAT); + //dev_info(hrng->dev, "done\r\n"); + /* + if (VIC_STAT_LAST_CMD(readl(hrng->base + VIC_STAT)) == + VIC_CTRL_CMD_GEN_RANDOM) { + dev_info(hrng->dev, "Need Update Buffer\r\n"); + } + */ + } + vic_rng_irq_mask_clear(hrng); + + return IRQ_HANDLED; +} + +static void vic_rng_cleanup(struct hwrng *rng) +{ + struct vic_rng *hrng = to_vic_rng(rng); + + writel(0, hrng->base + VIC_CTRL); +} + +static int vic_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) +{ + struct vic_rng *hrng = to_vic_rng(rng); + + vic_trng_cmd(hrng, VIC_CTRL_CMD_ZEROIZE); + vic_trng_cmd(hrng, VIC_CTRL_CMD_GEN_NOISE); + vic_trng_cmd(hrng, VIC_CTRL_CMD_CREATE_STATE); + + vic_wait_till_idle(hrng); + max = min_t(size_t, max, (VIC_RAND_LEN * 4)); + + writel(0x0, hrng->base + VIC_MODE); + vic_trng_cmd(hrng, VIC_CTRL_CMD_GEN_RANDOM); + + vic_wait_till_idle(hrng); + memcpy_fromio(buf, hrng->base + VIC_RAND0, max); + vic_trng_cmd(hrng, VIC_CTRL_CMD_ZEROIZE); + + vic_wait_till_idle(hrng); + return max; +} + +static int vic_rng_probe(struct platform_device *pdev) +{ + int ret; + int irq; + struct vic_rng *rng; + struct resource *res; + + rng = devm_kzalloc(&pdev->dev, sizeof(*rng), GFP_KERNEL); + if (!rng){ + return -ENOMEM; + } + + platform_set_drvdata(pdev, rng); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + rng->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(rng->base)){ + return PTR_ERR(rng->base); + } + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) { + dev_err(&pdev->dev, "Couldn't get irq %d\n", irq); + return irq; + } + + ret = devm_request_irq(&pdev->dev, irq, vic_rng_irq, 0, pdev->name, + (void *)rng); + if (ret) { + dev_err(&pdev->dev, "Can't get interrupt working.\n"); + return ret; + } + + rng->rng.name = pdev->name; + rng->rng.init = vic_rng_init; + rng->rng.cleanup = vic_rng_cleanup; + rng->rng.read = vic_rng_read; + + rng->dev = &pdev->dev; + + ret = devm_hwrng_register(&pdev->dev, &rng->rng); + if (ret) { + dev_err(&pdev->dev, "failed to register hwrng\n"); + return ret; + } + + dev_info(&pdev->dev, "Initialized\n"); + + return 0; +} + +static const struct of_device_id vic_rng_dt_ids[] = { + { .compatible = "starfive,vic-rng" }, + { } +}; +MODULE_DEVICE_TABLE(of, vic_rng_dt_ids); + +static struct platform_driver vic_rng_driver = { + .probe = vic_rng_probe, + .driver = { + .name = "vic-rng", + .of_match_table = of_match_ptr(vic_rng_dt_ids), + }, +}; + +module_platform_driver(vic_rng_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Huan Feng "); +MODULE_DESCRIPTION("Starfive VIC random number generator driver"); diff --git a/drivers/char/hw_random/starfive-vic-rng.h b/drivers/char/hw_random/starfive-vic-rng.h new file mode 100644 index 0000000000000..b3bbabde0cfb1 --- /dev/null +++ b/drivers/char/hw_random/starfive-vic-rng.h @@ -0,0 +1,167 @@ +/* + ****************************************************************************** + * @file starfive-vic-rng.h + * @author StarFive Technology + * @version V1.0 + * @date 08/13/2020 + * @brief + ****************************************************************************** + * @copy + * + * THE PRESENT SOFTWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STARFIVE SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH SOFTWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + * COPYRIGHT 2020 Shanghai StarFive Technology Co., Ltd. + */ + +#define VIC_CTRL 0x00 +#define VIC_MODE 0x04 +#define VIC_SMODE 0x08 +#define VIC_STAT 0x0C +#define VIC_IE 0x10 +#define VIC_ISTAT 0x14 +#define VIC_ALARM 0x18 +#define VIC_BUILD_ID 0x1C +#define VIC_FEATURES 0x20 +#define VIC_RAND0 0x24 +#define VIC_NPA_DATA0 0x34 +#define VIC_SEED0 0x74 +#define VIC_IA_RDATA 0xA4 +#define VIC_IA_WDATA 0xA8 +#define VIC_IA_ADDR 0xAC +#define VIC_IA_CMD 0xB0 + +/* CTRL */ +#define VIC_CTRL_CMD_NOP 0 +#define VIC_CTRL_CMD_GEN_NOISE 1 +#define VIC_CTRL_CMD_GEN_NONCE 2 +#define VIC_CTRL_CMD_CREATE_STATE 3 +#define VIC_CTRL_CMD_RENEW_STATE 4 +#define VIC_CTRL_CMD_REFRESH_ADDIN 5 +#define VIC_CTRL_CMD_GEN_RANDOM 6 +#define VIC_CTRL_CMD_ADVANCE_STATE 7 +#define VIC_CTRL_CMD_KAT 8 +#define VIC_CTRL_CMD_ZEROIZE 15 + +/* MODE */ +#define _VIC_MODE_ADDIN_PRESENT 4 +#define _VIC_MODE_PRED_RESIST 3 +#define _VIC_MODE_KAT_SEL 2 +#define _VIC_MODE_KAT_VEC 1 +#define _VIC_MODE_SEC_ALG 0 + +#define VIC_MODE_ADDIN_PRESENT (1UL << _VIC_MODE_ADDIN_PRESENT) +#define VIC_MODE_PRED_RESIST (1UL << _VIC_MODE_PRED_RESIST) +#define VIC_MODE_KAT_SEL (1UL << _VIC_MODE_KAT_SEL) +#define VIC_MODE_KAT_VEC (1UL << _VIC_MODE_KAT_VEC) +#define VIC_MODE_SEC_ALG (1UL << _VIC_MODE_SEC_ALG) + +/* SMODE */ +#define _VIC_SMODE_MAX_REJECTS 2 +#define _VIC_SMODE_SECURE_EN 1 +#define _VIC_SMODE_NONCE 0 + +#define VIC_SMODE_MAX_REJECTS(x) ((x) << _VIC_SMODE_MAX_REJECTS) +#define VIC_SMODE_SECURE_EN(x) ((x) << _VIC_SMODE_SECURE_EN) +#define VIC_SMODE_NONCE (1UL << _VIC_SMODE_NONCE) + +/* STAT */ +#define _VIC_STAT_BUSY 31 +#define _VIC_STAT_DRBG_STATE 7 +#define _VIC_STAT_SECURE 6 +#define _VIC_STAT_NONCE_MODE 5 +#define _VIC_STAT_SEC_ALG 4 +#define _VIC_STAT_LAST_CMD 0 + +#define VIC_STAT_BUSY (1UL << _VIC_STAT_BUSY) +#define VIC_STAT_DRBG_STATE (1UL << _VIC_STAT_DRBG_STATE) +#define VIC_STAT_SECURE (1UL << _VIC_STAT_SECURE) +#define VIC_STAT_NONCE_MODE (1UL << _VIC_STAT_NONCE_MODE) +#define VIC_STAT_SEC_ALG (1UL << _VIC_STAT_SEC_ALG) +#define VIC_STAT_LAST_CMD(x) (((x) >> _VIC_STAT_LAST_CMD) & 0xF) + +/* IE */ +#define _VIC_IE_GLBL 31 +#define _VIC_IE_DONE 4 +#define _VIC_IE_ALARMS 3 +#define _VIC_IE_NOISE_RDY 2 +#define _VIC_IE_KAT_COMPLETE 1 +#define _VIC_IE_ZEROIZE 0 + +#define VIC_IE_GLBL (1UL << _VIC_IE_GLBL) +#define VIC_IE_DONE (1UL << _VIC_IE_DONE) +#define VIC_IE_ALARMS (1UL << _VIC_IE_ALARMS) +#define VIC_IE_NOISE_RDY (1UL << _VIC_IE_NOISE_RDY) +#define VIC_IE_KAT_COMPLETE (1UL << _VIC_IE_KAT_COMPLETE) +#define VIC_IE_ZEROIZE (1UL << _VIC_IE_ZEROIZE) +#define VIC_IE_ALL (VIC_IE_GLBL | VIC_IE_DONE | VIC_IE_ALARMS | \ + VIC_IE_NOISE_RDY | VIC_IE_KAT_COMPLETE | VIC_IE_ZEROIZE) + +/* ISTAT */ +#define _VIC_ISTAT_DONE 4 +#define _VIC_ISTAT_ALARMS 3 +#define _VIC_ISTAT_NOISE_RDY 2 +#define _VIC_ISTAT_KAT_COMPLETE 1 +#define _VIC_ISTAT_ZEROIZE 0 + +#define VIC_ISTAT_DONE (1UL << _VIC_ISTAT_DONE) +#define VIC_ISTAT_ALARMS (1UL << _VIC_ISTAT_ALARMS) +#define VIC_ISTAT_NOISE_RDY (1UL << _VIC_ISTAT_NOISE_RDY) +#define VIC_ISTAT_KAT_COMPLETE (1UL << _VIC_ISTAT_KAT_COMPLETE) +#define VIC_ISTAT_ZEROIZE (1UL << _VIC_ISTAT_ZEROIZE) + +/* ALARMS */ +#define VIC_ALARM_ILLEGAL_CMD_SEQ (1UL << 4) +#define VIC_ALARM_FAILED_TEST_ID_OK 0 +#define VIC_ALARM_FAILED_TEST_ID_KAT_STAT 1 +#define VIC_ALARM_FAILED_TEST_ID_KAT 2 +#define VIC_ALARM_FAILED_TEST_ID_MONOBIT 3 +#define VIC_ALARM_FAILED_TEST_ID_RUN 4 +#define VIC_ALARM_FAILED_TEST_ID_LONGRUN 5 +#define VIC_ALARM_FAILED_TEST_ID_AUTOCORRELATION 6 +#define VIC_ALARM_FAILED_TEST_ID_POKER 7 +#define VIC_ALARM_FAILED_TEST_ID_REPETITION_COUNT 8 +#define VIC_ALARM_FAILED_TEST_ID_ADAPATIVE_PROPORTION 9 + +/* BUILD_ID */ +#define VIC_BUILD_ID_STEPPING(x) (((x) >> 28) & 0xF) +#define VIC_BUILD_ID_EPN(x) ((x) & 0xFFFF) + +/* FEATURES */ +#define VIC_FEATURES_AES_256(x) (((x) >> 9) & 1) +#define VIC_FEATURES_EXTRA_PS_PRESENT(x) (((x) >> 8) & 1) +#define VIC_FEATURES_DIAG_LEVEL_NS(x) (((x) >> 7) & 1) +#define VIC_FEATURES_DIAG_LEVEL_CLP800(x) (((x) >> 4) & 7) +#define VIC_FEATURES_DIAG_LEVEL_ST_HLT(x) (((x) >> 1) & 7) +#define VIC_FEATURES_SECURE_RST_STATE(x) ((x) & 1) + +/* IA_CMD */ +#define VIC_IA_CMD_GO (1UL << 31) +#define VIC_IA_CMD_WR (1) + +#define _VIC_SMODE_MAX_REJECTS_MASK 255UL +#define _VIC_SMODE_SECURE_EN_MASK 1UL +#define _VIC_SMODE_NONCE_MASK 1UL +#define _VIC_MODE_SEC_ALG_MASK 1UL +#define _VIC_MODE_ADDIN_PRESENT_MASK 1UL +#define _VIC_MODE_PRED_RESIST_MASK 1UL + +#define VIC_SMODE_SET_MAX_REJECTS(y, x) (((y) & ~(_VIC_SMODE_MAX_REJECTS_MASK << _VIC_SMODE_MAX_REJECTS)) | ((x) << _VIC_SMODE_MAX_REJECTS)) +#define VIC_SMODE_SET_SECURE_EN(y, x) (((y) & ~(_VIC_SMODE_SECURE_EN_MASK << _VIC_SMODE_SECURE_EN)) | ((x) << _VIC_SMODE_SECURE_EN)) +#define VIC_SMODE_SET_NONCE(y, x) (((y) & ~(_VIC_SMODE_NONCE_MASK << _VIC_SMODE_NONCE)) | ((x) << _VIC_SMODE_NONCE)) +#define VIC_SMODE_GET_MAX_REJECTS(x) (((x) >> _VIC_SMODE_MAX_REJECTS) & _VIC_SMODE_MAX_REJECTS_MASK) +#define VIC_SMODE_GET_SECURE_EN(x) (((x) >> _VIC_SMODE_SECURE_EN) & _VIC_SMODE_SECURE_EN_MASK) +#define VIC_SMODE_GET_NONCE(x) (((x) >> _VIC_SMODE_NONCE) & _VIC_SMODE_NONCE_MASK) + +#define VIC_MODE_SET_SEC_ALG(y, x) (((y) & ~(_VIC_MODE_SEC_ALG_MASK << _VIC_MODE_SEC_ALG)) | ((x) << _VIC_MODE_SEC_ALG)) +#define VIC_MODE_SET_PRED_RESIST(y, x) (((y) & ~(_VIC_MODE_PRED_RESIST_MASK << _VIC_MODE_PRED_RESIST)) | ((x) << _VIC_MODE_PRED_RESIST)) +#define VIC_MODE_SET_ADDIN_PRESENT(y, x) (((y) & ~(_VIC_MODE_ADDIN_PRESENT_MASK << _VIC_MODE_ADDIN_PRESENT)) | ((x) << _VIC_MODE_ADDIN_PRESENT)) +#define VIC_MODE_GET_SEC_ALG(x) (((x) >> _VIC_MODE_SEC_ALG) & _VIC_MODE_SEC_ALG_MASK) +#define VIC_MODE_GET_PRED_RESIST(x) (((x) >> _VIC_MODE_PRED_RESIST) & _VIC_MODE_PRED_RESIST_MASK) +#define VIC_MODE_GET_ADDIN_PRESENT(x) (((x) >> _VIC_MODE_ADDIN_PRESENT) & _VIC_MODE_ADDIN_PRESENT_MASK) + +#define VIC_RAND_LEN 4 From b35b122b692f43429d38ece0e0e9ee44a8d8cc62 Mon Sep 17 00:00:00 2001 From: Tom Date: Fri, 8 Jan 2021 02:54:51 +0800 Subject: [PATCH 46/80] sifive/sifive_l2_cache: Add sifive_l2_flush64_range function --- drivers/soc/sifive/Kconfig | 15 ++++++++++ drivers/soc/sifive/sifive_l2_cache.c | 41 +++++++++++++++++++++++++++- include/soc/sifive/sifive_l2_cache.h | 4 +++ 3 files changed, 59 insertions(+), 1 deletion(-) diff --git a/drivers/soc/sifive/Kconfig b/drivers/soc/sifive/Kconfig index 58cf8c40d08d5..4d0fdab56e81a 100644 --- a/drivers/soc/sifive/Kconfig +++ b/drivers/soc/sifive/Kconfig @@ -7,4 +7,19 @@ config SIFIVE_L2 help Support for the L2 cache controller on SiFive platforms. +config SIFIVE_L2_FLUSH + bool "Support Level 2 Cache Controller Flush operation of SiFive Soc" + +if SIFIVE_L2_FLUSH + +config SIFIVE_L2_FLUSH_START + hex "Level 2 Cache Flush operation start" + default 0x80000000 + +config SIFIVE_L2_FLUSH_SIZE + hex "Level 2 Cache Flush operation size" + default 0x800000000 + +endif # SIFIVE_L2_FLUSH + endif diff --git a/drivers/soc/sifive/sifive_l2_cache.c b/drivers/soc/sifive/sifive_l2_cache.c index 59640a1d0b28a..0b9e9e852ee44 100644 --- a/drivers/soc/sifive/sifive_l2_cache.c +++ b/drivers/soc/sifive/sifive_l2_cache.c @@ -29,13 +29,17 @@ #define SIFIVE_L2_DATECCFAIL_HIGH 0x164 #define SIFIVE_L2_DATECCFAIL_COUNT 0x168 +#define SIFIVE_L2_FLUSH64 0x200 + #define SIFIVE_L2_CONFIG 0x00 #define SIFIVE_L2_WAYENABLE 0x08 #define SIFIVE_L2_ECCINJECTERR 0x40 #define SIFIVE_L2_MAX_ECCINTR 4 -static void __iomem *l2_base; +#define SIFIVE_L2_FLUSH64_LINE_LEN 64 + +static void __iomem *l2_base = NULL; static int g_irq[SIFIVE_L2_MAX_ECCINTR]; static struct riscv_cacheinfo_ops l2_cache_ops; @@ -116,6 +120,41 @@ int unregister_sifive_l2_error_notifier(struct notifier_block *nb) } EXPORT_SYMBOL_GPL(unregister_sifive_l2_error_notifier); +#ifdef CONFIG_SIFIVE_L2_FLUSH +void sifive_l2_flush64_range(unsigned long start, unsigned long len) +{ + unsigned long line; + + if(!l2_base) { + pr_warn("L2CACHE: base addr invalid, skipping flush\n"); + return; + } + + /* TODO: if (len == 0), skipping flush or going on? */ + if(!len) { + pr_debug("L2CACHE: flush64 range @ 0x%lx(len:0)\n", start); + return; + } + + /* make sure the address is in the range */ + if(start < CONFIG_SIFIVE_L2_FLUSH_START || + (start + len) > (CONFIG_SIFIVE_L2_FLUSH_START + + CONFIG_SIFIVE_L2_FLUSH_SIZE)) { + pr_warn("L2CACHE: flush64 out of range: %lx(%lx), skip flush\n", + start, len); + return; + } + + mb(); /* sync */ + for (line = start; line < start + len; + line += SIFIVE_L2_FLUSH64_LINE_LEN) { + writeq(line, l2_base + SIFIVE_L2_FLUSH64); + mb(); + } +} +EXPORT_SYMBOL_GPL(sifive_l2_flush64_range); +#endif + static int l2_largest_wayenabled(void) { return readl(l2_base + SIFIVE_L2_WAYENABLE) & 0xFF; diff --git a/include/soc/sifive/sifive_l2_cache.h b/include/soc/sifive/sifive_l2_cache.h index 92ade10ed67e9..dd3e56787d316 100644 --- a/include/soc/sifive/sifive_l2_cache.h +++ b/include/soc/sifive/sifive_l2_cache.h @@ -7,6 +7,10 @@ #ifndef __SOC_SIFIVE_L2_CACHE_H #define __SOC_SIFIVE_L2_CACHE_H +#ifdef CONFIG_SIFIVE_L2_FLUSH +extern void sifive_l2_flush64_range(unsigned long start, unsigned long len); +#endif + extern int register_sifive_l2_error_notifier(struct notifier_block *nb); extern int unregister_sifive_l2_error_notifier(struct notifier_block *nb); From f521121cf982b3365d57045270549dd6add8a38c Mon Sep 17 00:00:00 2001 From: Tom Date: Mon, 15 Feb 2021 23:59:46 +0800 Subject: [PATCH 47/80] sifive/sifive_l2_cache: Add Starfive support --- drivers/soc/sifive/sifive_l2_cache.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soc/sifive/sifive_l2_cache.c b/drivers/soc/sifive/sifive_l2_cache.c index 0b9e9e852ee44..5f2b295fc5efd 100644 --- a/drivers/soc/sifive/sifive_l2_cache.c +++ b/drivers/soc/sifive/sifive_l2_cache.c @@ -103,6 +103,7 @@ static void l2_config_read(void) static const struct of_device_id sifive_l2_ids[] = { { .compatible = "sifive,fu540-c000-ccache" }, { .compatible = "sifive,fu740-c000-ccache" }, + { .compatible = "starfive,ccache0" }, { /* end of table */ }, }; From e090c479af4295ae462091174eac15ec24f24fc9 Mon Sep 17 00:00:00 2001 From: Tom Date: Sat, 13 Feb 2021 22:25:17 +0800 Subject: [PATCH 48/80] sifive/sifive_l2_cache: Add disabling IRQ option (workaround) --- drivers/irqchip/irq-sifive-plic.c | 41 ++++++++++++++++++++++++++++ drivers/soc/sifive/Kconfig | 4 +++ drivers/soc/sifive/sifive_l2_cache.c | 8 ++++++ 3 files changed, 53 insertions(+) diff --git a/drivers/irqchip/irq-sifive-plic.c b/drivers/irqchip/irq-sifive-plic.c index 259065d271ef0..5e002f24d4dc7 100644 --- a/drivers/irqchip/irq-sifive-plic.c +++ b/drivers/irqchip/irq-sifive-plic.c @@ -277,6 +277,44 @@ static int plic_starting_cpu(unsigned int cpu) return 0; } +#if IS_ENABLED(CONFIG_SIFIVE_L2_IRQ_DISABLE) +#ifdef CONFIG_SOC_STARFIVE +#define SIFIVE_L2_MAX_ECCINTR 4 +#else +#define SIFIVE_L2_MAX_ECCINTR 3 +#endif +static const struct of_device_id sifive_l2_ids[] = { + { .compatible = "sifive,fu540-c000-ccache" }, + { .compatible = "starfive,ccache0" }, + { /* end of table */ }, +}; + +static void sifive_l2_irq_disable(struct plic_handler *handler) +{ + int i, irq; + struct of_phandle_args oirq; + + struct device_node *np = of_find_matching_node(NULL, sifive_l2_ids); + if (!np) { + pr_err("Can't get L2 cache device node.\n"); + return; + } + + for (i = 0; i < SIFIVE_L2_MAX_ECCINTR; i++) { + if (!of_irq_parse_one(np, i, &oirq)) { + irq = *oirq.args; + if (irq) { + pr_info("disable L2 cache irq %d in plic\n", irq); + plic_toggle(handler, irq, 0); + continue; + } + } + pr_err("Can't get L2 cache irq(#%d).\n", i); + } +} +#endif + + static int __init plic_init(struct device_node *node, struct device_node *parent) { @@ -370,6 +408,9 @@ static int __init plic_init(struct device_node *node, done: for (hwirq = 1; hwirq <= nr_irqs; hwirq++) plic_toggle(handler, hwirq, 0); +#if IS_ENABLED(CONFIG_SIFIVE_L2_IRQ_DISABLE) + sifive_l2_irq_disable(handler); +#endif nr_handlers++; } diff --git a/drivers/soc/sifive/Kconfig b/drivers/soc/sifive/Kconfig index 4d0fdab56e81a..6f8556df62952 100644 --- a/drivers/soc/sifive/Kconfig +++ b/drivers/soc/sifive/Kconfig @@ -22,4 +22,8 @@ config SIFIVE_L2_FLUSH_SIZE endif # SIFIVE_L2_FLUSH +config SIFIVE_L2_IRQ_DISABLE + bool "Disable Level 2 Cache Controller interrupts" + default y if SOC_STARFIVE + endif diff --git a/drivers/soc/sifive/sifive_l2_cache.c b/drivers/soc/sifive/sifive_l2_cache.c index 5f2b295fc5efd..be4e141f5a0ea 100644 --- a/drivers/soc/sifive/sifive_l2_cache.c +++ b/drivers/soc/sifive/sifive_l2_cache.c @@ -40,7 +40,9 @@ #define SIFIVE_L2_FLUSH64_LINE_LEN 64 static void __iomem *l2_base = NULL; +#if !IS_ENABLED(CONFIG_SIFIVE_L2_IRQ_DISABLE) static int g_irq[SIFIVE_L2_MAX_ECCINTR]; +#endif static struct riscv_cacheinfo_ops l2_cache_ops; enum { @@ -188,6 +190,7 @@ static const struct attribute_group *l2_get_priv_group(struct cacheinfo *this_le return NULL; } +#if !IS_ENABLED(CONFIG_SIFIVE_L2_IRQ_DISABLE) static irqreturn_t l2_int_handler(int irq, void *device) { unsigned int add_h, add_l; @@ -231,12 +234,15 @@ static irqreturn_t l2_int_handler(int irq, void *device) return IRQ_HANDLED; } +#endif static int __init sifive_l2_init(void) { struct device_node *np; struct resource res; +#if !IS_ENABLED(CONFIG_SIFIVE_L2_IRQ_DISABLE) int i, rc, intr_num; +#endif np = of_find_matching_node(NULL, sifive_l2_ids); if (!np) @@ -249,6 +255,7 @@ static int __init sifive_l2_init(void) if (!l2_base) return -ENOMEM; +#if !IS_ENABLED(CONFIG_SIFIVE_L2_IRQ_DISABLE) intr_num = of_property_count_u32_elems(np, "interrupts"); if (!intr_num) { pr_err("L2CACHE: no interrupts property\n"); @@ -263,6 +270,7 @@ static int __init sifive_l2_init(void) return rc; } } +#endif l2_config_read(); From 4e0ad81893ebd746eaf1115a2f0429a50152160a Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 21 May 2021 08:35:33 +0200 Subject: [PATCH 49/80] sifive/sifive_l2_cache: Print a backtrace on out-of-range flushes This makes it easier to find out which driver passes a wrong address range. Signed-off-by: Geert Uytterhoeven --- drivers/soc/sifive/sifive_l2_cache.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/soc/sifive/sifive_l2_cache.c b/drivers/soc/sifive/sifive_l2_cache.c index be4e141f5a0ea..626b664547e51 100644 --- a/drivers/soc/sifive/sifive_l2_cache.c +++ b/drivers/soc/sifive/sifive_l2_cache.c @@ -143,8 +143,8 @@ void sifive_l2_flush64_range(unsigned long start, unsigned long len) if(start < CONFIG_SIFIVE_L2_FLUSH_START || (start + len) > (CONFIG_SIFIVE_L2_FLUSH_START + CONFIG_SIFIVE_L2_FLUSH_SIZE)) { - pr_warn("L2CACHE: flush64 out of range: %lx(%lx), skip flush\n", - start, len); + WARN(1, "L2CACHE: flush64 out of range: %lx(%lx), skip flush\n", + start, len); return; } From 79f7e2e390ce12734b572a89ee4f2503cc114ca1 Mon Sep 17 00:00:00 2001 From: Atish Patra Date: Sat, 12 Jun 2021 16:52:26 -0700 Subject: [PATCH 50/80] sifive/sifive_l2_cache: Align the address to cache line [Emil: fix suggested by Geert Uytterhoeven ] Signed-off-by: Atish Patra Signed-off-by: Emil Renner Berthing --- drivers/soc/sifive/sifive_l2_cache.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/soc/sifive/sifive_l2_cache.c b/drivers/soc/sifive/sifive_l2_cache.c index 626b664547e51..fb39be6c730a9 100644 --- a/drivers/soc/sifive/sifive_l2_cache.c +++ b/drivers/soc/sifive/sifive_l2_cache.c @@ -139,6 +139,9 @@ void sifive_l2_flush64_range(unsigned long start, unsigned long len) return; } + len = len + (start % SIFIVE_L2_FLUSH64_LINE_LEN); + start = ALIGN_DOWN(start, SIFIVE_L2_FLUSH64_LINE_LEN); + /* make sure the address is in the range */ if(start < CONFIG_SIFIVE_L2_FLUSH_START || (start + len) > (CONFIG_SIFIVE_L2_FLUSH_START + From f6ef3e52bb48a943cec73adeebb2dae3ea3aee49 Mon Sep 17 00:00:00 2001 From: Samin Guo Date: Fri, 8 Jan 2021 03:11:04 +0800 Subject: [PATCH 51/80] drivers/tty/serial/8250: update driver for JH7100 --- drivers/tty/serial/8250/8250_port.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index 46e2079ad1aa2..07b58b04a889e 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -73,8 +73,16 @@ static const struct serial8250_config uart_config[] = { }, [PORT_16550] = { .name = "16550", +#ifdef CONFIG_SOC_STARFIVE + .fifo_size = 16, + .tx_loadsz = 16, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_00, + .rxtrig_bytes = {1, 4, 8, 14}, + .flags = UART_CAP_FIFO, +#else .fifo_size = 1, .tx_loadsz = 1, +#endif }, [PORT_16550A] = { .name = "16550A", From 5bf95df02c471bd17b2fc8207759660aa7ccf821 Mon Sep 17 00:00:00 2001 From: Chenjieqin Date: Fri, 8 Jan 2021 03:56:54 +0800 Subject: [PATCH 52/80] drivers/pwm: Add SiFive PWM PTC driver --- drivers/pwm/Kconfig | 11 ++ drivers/pwm/Makefile | 1 + drivers/pwm/pwm-sifive-ptc.c | 291 +++++++++++++++++++++++++++++++++++ 3 files changed, 303 insertions(+) create mode 100644 drivers/pwm/pwm-sifive-ptc.c diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 21e3b05a5153a..04dfad1b68dba 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -494,6 +494,17 @@ config PWM_SIFIVE To compile this driver as a module, choose M here: the module will be called pwm-sifive. +config PWM_SIFIVE_PTC + tristate "SiFive PWM PTC support" + depends on SOC_SIFIVE || SOC_STARFIVE || COMPILE_TEST + depends on OF + depends on COMMON_CLK + help + Generic PWM framework driver for SiFive SoCs. + + To compile this driver as a module, choose M here: the module + will be called pwm-sifive-ptc. + config PWM_SL28CPLD tristate "Kontron sl28cpld PWM support" depends on MFD_SL28CPLD || COMPILE_TEST diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index 708840b7fba8d..3ad7903fd7fe2 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -45,6 +45,7 @@ obj-$(CONFIG_PWM_RENESAS_TPU) += pwm-renesas-tpu.o obj-$(CONFIG_PWM_ROCKCHIP) += pwm-rockchip.o obj-$(CONFIG_PWM_SAMSUNG) += pwm-samsung.o obj-$(CONFIG_PWM_SIFIVE) += pwm-sifive.o +obj-$(CONFIG_PWM_SIFIVE_PTC) += pwm-sifive-ptc.o obj-$(CONFIG_PWM_SL28CPLD) += pwm-sl28cpld.o obj-$(CONFIG_PWM_SPEAR) += pwm-spear.o obj-$(CONFIG_PWM_SPRD) += pwm-sprd.o diff --git a/drivers/pwm/pwm-sifive-ptc.c b/drivers/pwm/pwm-sifive-ptc.c new file mode 100644 index 0000000000000..599059805e87a --- /dev/null +++ b/drivers/pwm/pwm-sifive-ptc.c @@ -0,0 +1,291 @@ +/* + * Copyright (C) 2018 SiFive, Inc + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2, as published by + * the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define PTC_DEBUG 0 + +/* max channel of pwm */ +#define MAX_PWM 8 + +/* PTC Register offsets */ +#define REG_RPTC_CNTR 0x0 +#define REG_RPTC_HRC 0x4 +#define REG_RPTC_LRC 0x8 +#define REG_RPTC_CTRL 0xC + +/* Bit for PWM clock */ +#define BIT_PWM_CLOCK_EN 31 + +/* Bit for clock gen soft reset */ +#define BIT_CLK_GEN_SOFT_RESET 13 + +#define NS_1 1000000000 + +/* Access PTC register (cntr hrc lrc and ctrl) ,need to replace PWM_BASE_ADDR */ +#define REG_PTC_BASE_ADDR_SUB(base, N) ((base) + ((N>3)?((N-4)*0x10+(1<<15)):(N*0x10))) +#define REG_PTC_RPTC_CNTR(base,N) (REG_PTC_BASE_ADDR_SUB(base,N)) +#define REG_PTC_RPTC_HRC(base,N) (REG_PTC_BASE_ADDR_SUB(base,N) + 0x4) +#define REG_PTC_RPTC_LRC(base,N) (REG_PTC_BASE_ADDR_SUB(base,N) + 0x8) +#define REG_PTC_RPTC_CTRL(base,N) (REG_PTC_BASE_ADDR_SUB(base,N) + 0xC) + +/* pwm ptc device */ +struct sifive_pwm_ptc_device { + struct pwm_chip chip; + struct clk *clk; + void __iomem *regs; + int irq; + /* apb clock frequency , from dts */ + unsigned int approx_period; +}; + +static inline struct sifive_pwm_ptc_device *chip_to_sifive_ptc(struct pwm_chip *c) +{ + return container_of(c, struct sifive_pwm_ptc_device, chip); +} + + +static void sifive_pwm_ptc_get_state(struct pwm_chip *chip, struct pwm_device *dev, struct pwm_state *state) +{ + struct sifive_pwm_ptc_device *pwm = chip_to_sifive_ptc(chip); + uint32_t data_lrc; + uint32_t data_hrc; + uint32_t pwm_clk_ns = 0; + + /* get lrc and hrc data from registe*/ + data_lrc = ioread32(REG_PTC_RPTC_LRC(pwm->regs, dev->hwpwm)); + data_hrc = ioread32(REG_PTC_RPTC_HRC(pwm->regs, dev->hwpwm)); + + /* how many ns does apb clock elapse */ + pwm_clk_ns = NS_1 / pwm->approx_period; + + /* pwm period(ns) */ + state->period = data_lrc*pwm_clk_ns; + + /* duty cycle(ns) ,means high level eclapse ns if it is normal polarity */ + state->duty_cycle = data_hrc*pwm_clk_ns; + + /* polarity,we don't use it now because it is not in dts */ + state->polarity = PWM_POLARITY_NORMAL; + + /* enabled or not */ + state->enabled = 1; +#ifdef PTC_DEBUG + printk("sifive_pwm_ptc_get_state in,no:%d....\r\n",dev->hwpwm); + printk("data_hrc:0x%x 0x%x \n", data_hrc, data_lrc); + printk("period:%llu\r\n",state->period); + printk("duty_cycle:%llu\r\n",state->duty_cycle); + printk("polarity:%d\r\n",state->polarity); + printk("enabled:%d\r\n",state->enabled); +#endif +} + + +static int sifive_pwm_ptc_apply(struct pwm_chip *chip, struct pwm_device *dev, struct pwm_state *state) +{ + struct sifive_pwm_ptc_device *pwm = chip_to_sifive_ptc(chip); + uint32_t pwm_clk_ns = 0; + uint32_t data_hrc = 0; + uint32_t data_lrc = 0; + uint32_t period_data = 0; + uint32_t duty_data = 0; + void __iomem* reg_addr; + +#if PTC_DEBUG + printk("sifive_pwm_ptc_apply in,no:%d....\r\n",dev->hwpwm); + printk("set parameter......\r\n"); + printk("period:%d\r\n",state->period); + printk("duty_cycle:%d\r\n",state->duty_cycle); + printk("polarity:%d\r\n",state->polarity); + printk("enabled:%d\r\n",state->enabled); +#endif + /* duty_cycle should be less or equal than period */ + if(state->duty_cycle > state->period) + state->duty_cycle = state->period; + + /* calculate pwm real period (ns) */ + pwm_clk_ns = NS_1 / pwm->approx_period; + +#if PTC_DEBUG + printk("approx_period,:%d,pwm_clk_ns:%d\r\n",pwm->approx_period,pwm_clk_ns); +#endif + + /* calculate period count */ + period_data = state->period / pwm_clk_ns; + + if (!state->enabled) + /* if is unenable,just set duty_dat to 0 , means low level always */ + duty_data = 0; + else + /* calculate duty count*/ + duty_data = state->duty_cycle / pwm_clk_ns; + +#if PTC_DEBUG + printk("period_data:%d,duty_data:%d\r\n",period_data,duty_data); +#endif + + if(state->polarity == PWM_POLARITY_NORMAL) + /* calculate data_hrc */ + data_hrc = period_data - duty_data; + else + /* calculate data_hrc */ + data_hrc = duty_data; + + data_lrc = period_data; + + /* set hrc */ + reg_addr = REG_PTC_RPTC_HRC(pwm->regs, dev->hwpwm); +#if PTC_DEBUG + printk("[sifive_pwm_ptc_config]reg_addr:0x%lx,data:%d....\n",reg_addr,data_hrc); +#endif + iowrite32(data_hrc, reg_addr); + +#if PTC_DEBUG + printk("[sifive_pwm_ptc_config]hrc ok....\n"); +#endif + + /* set lrc */ + reg_addr = REG_PTC_RPTC_LRC(pwm->regs, dev->hwpwm); +#if PTC_DEBUG + printk("[sifive_pwm_ptc_config]reg_addr:0x%lx,data:%d....\n",reg_addr,data_lrc); +#endif + + iowrite32(data_lrc, reg_addr); + +#if PTC_DEBUG + printk("[sifive_pwm_ptc_config]lrc ok....\n"); +#endif + + return 0; +} + + + +static const struct pwm_ops sifive_pwm_ptc_ops = { + .get_state = sifive_pwm_ptc_get_state, + .apply = (void *)sifive_pwm_ptc_apply, + .owner = THIS_MODULE, +}; + + + + +static int sifive_pwm_ptc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *node = pdev->dev.of_node; + struct sifive_pwm_ptc_device *pwm; + struct pwm_chip *chip; + struct resource *res; + int ret; + +#if PTC_DEBUG + printk("sifive_pwm_ptc_probe in....\r\n"); +#endif + pwm = devm_kzalloc(dev, sizeof(*pwm), GFP_KERNEL); + if (!pwm) { + dev_err(dev, "Out of memory\n"); + return -ENOMEM; + } + + chip = &pwm->chip; + chip->dev = dev; + chip->ops = &sifive_pwm_ptc_ops; + + /* how many parameters can be transfered to ptc,need to fix */ + chip->of_pwm_n_cells = 3; + chip->base = -1; + + /* get pwm channels count, max value is 8 */ + ret = of_property_read_u32(node, "starfive,npwm", &chip->npwm); + if (ret < 0 || chip->npwm > MAX_PWM) + chip->npwm = MAX_PWM; + +#if PTC_DEBUG + printk("[sifive_pwm_ptc_probe] npwm:0x%lx....\r\n",chip->npwm); +#endif + /* get apb clock frequency */ + ret = of_property_read_u32(node, "sifive,approx-period", &pwm->approx_period); + +#if PTC_DEBUG + printk("[sifive_pwm_ptc_probe] approx_period:%d....\r\n",pwm->approx_period); +#endif + /* get IO base address*/ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + +#if PTC_DEBUG + printk("[sifive_pwm_ptc_probe] res start:0x%lx,end:0x%lx....\r\n",res->start,res->end); +#endif + pwm->regs = devm_ioremap_resource(dev, res); + if (IS_ERR(pwm->regs)) + { + dev_err(dev, "Unable to map IO resources\n"); + return PTR_ERR(pwm->regs); + } + +#if PTC_DEBUG + printk("[sifive_pwm_ptc_probe] regs:0x%lx....\r\n",pwm->regs); +#endif + + pwm->clk = devm_clk_get(dev, NULL); + if (IS_ERR(pwm->clk)) { + dev_err(dev, "Unable to find controller clock\n"); + return PTR_ERR(pwm->clk); + } + + /* after add,it will display as /sys/class/pwm/pwmchip0,0 is chip->base + * after execute echo 0 > export in , pwm0 can be seen */ + ret = pwmchip_add(chip); + if (ret < 0) { + dev_err(dev, "cannot register PTC: %d\n", ret); + return ret; + } + + platform_set_drvdata(pdev, pwm); + +#if PTC_DEBUG + printk("SiFive PWM PTC chip registered %d PWMs\n", chip->npwm); +#endif + + return 0; +} + +static int sifive_pwm_ptc_remove(struct platform_device *dev) +{ + struct sifive_pwm_ptc_device *pwm = platform_get_drvdata(dev); + struct pwm_chip *chip = &pwm->chip; + + pwmchip_remove(chip); + return 0; +} + +static const struct of_device_id sifive_pwm_ptc_of_match[] = { + { .compatible = "sifive,pwm0" }, + { .compatible = "starfive,pwm0" }, + { }, +}; +MODULE_DEVICE_TABLE(of, sifive_pwm_ptc_of_match); + +static struct platform_driver sifive_pwm_ptc_driver = { + .probe = sifive_pwm_ptc_probe, + .remove = sifive_pwm_ptc_remove, + .driver = { + .name = "pwm-sifive-ptc", + .of_match_table = of_match_ptr(sifive_pwm_ptc_of_match), + }, +}; +module_platform_driver(sifive_pwm_ptc_driver); + +MODULE_DESCRIPTION("SiFive PWM PTC driver"); +MODULE_LICENSE("GPL v2"); From 5f695a796347c235634f7da56f6af27ceef46a0a Mon Sep 17 00:00:00 2001 From: "yiming.li" Date: Tue, 16 Mar 2021 01:45:19 +0800 Subject: [PATCH 53/80] drivers/pwm/pwm-sifive-ptc: Clear PWM CNTR Clear CNTR of PWM after setting period & duty_cycle --- drivers/pwm/pwm-sifive-ptc.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/pwm/pwm-sifive-ptc.c b/drivers/pwm/pwm-sifive-ptc.c index 599059805e87a..29e29844799de 100644 --- a/drivers/pwm/pwm-sifive-ptc.c +++ b/drivers/pwm/pwm-sifive-ptc.c @@ -167,6 +167,10 @@ static int sifive_pwm_ptc_apply(struct pwm_chip *chip, struct pwm_device *dev, s printk("[sifive_pwm_ptc_config]lrc ok....\n"); #endif + /* Clear REG_RPTC_CNTR after setting period & duty_cycle*/ + reg_addr = REG_PTC_RPTC_CNTR(pwm->regs, dev->hwpwm); + iowrite32(0, reg_addr); + return 0; } From 1cf4fd375516ad4002b9db9757f08bce1e16cefa Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 27 May 2021 20:13:43 +0200 Subject: [PATCH 54/80] [WIP] dt-bindings: dma: dw-axi-dmac: Increase DMA channel limit to 16 The first DMAC instance in the StarFive JH7100 SoC supports 16 DMA channels. FIXME Given there are more changes to the driver than just increasing DMAC_MAX_CHANNELS, we probably need a new compatible value, too. Signed-off-by: Geert Uytterhoeven --- Documentation/devicetree/bindings/dma/snps,dw-axi-dmac.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/dma/snps,dw-axi-dmac.yaml b/Documentation/devicetree/bindings/dma/snps,dw-axi-dmac.yaml index 79e241498e253..09d3e9ee3939e 100644 --- a/Documentation/devicetree/bindings/dma/snps,dw-axi-dmac.yaml +++ b/Documentation/devicetree/bindings/dma/snps,dw-axi-dmac.yaml @@ -51,7 +51,7 @@ properties: dma-channels: minimum: 1 - maximum: 8 + maximum: 16 snps,dma-masters: description: | @@ -71,14 +71,14 @@ properties: Channel priority specifier associated with the DMA channels. $ref: /schemas/types.yaml#/definitions/uint32-array minItems: 1 - maxItems: 8 + maxItems: 16 snps,block-size: description: | Channel block size specifier associated with the DMA channels. $ref: /schemas/types.yaml#/definitions/uint32-array minItems: 1 - maxItems: 8 + maxItems: 16 snps,axi-max-burst-len: description: | From 15f35d6947cdc6a402b126d58f892a4b3633827c Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Fri, 17 Dec 2021 14:47:47 +0100 Subject: [PATCH 55/80] dmaengine: dw-axi-dmac: Fix RMW on channel suspend register Found by comparing the parallel implementation of more than 8 channel support for the StarFive JH7100 SoC by Samin. Fixes: 824351668a41 ("dmaengine: dw-axi-dmac: support DMAX_NUM_CHANNELS > 8") Co-developed-by: Samin Guo Signed-off-by: Samin Guo Signed-off-by: Emil Renner Berthing --- drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c index 33baf1591a490..4743fc8bdfc15 100644 --- a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c +++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c @@ -1164,8 +1164,9 @@ static int dma_chan_pause(struct dma_chan *dchan) BIT(chan->id) << DMAC_CHAN_SUSP_WE_SHIFT; axi_dma_iowrite32(chan->chip, DMAC_CHEN, val); } else { - val = BIT(chan->id) << DMAC_CHAN_SUSP2_SHIFT | - BIT(chan->id) << DMAC_CHAN_SUSP2_WE_SHIFT; + val = axi_dma_ioread32(chan->chip, DMAC_CHSUSPREG); + val |= BIT(chan->id) << DMAC_CHAN_SUSP2_SHIFT | + BIT(chan->id) << DMAC_CHAN_SUSP2_WE_SHIFT; axi_dma_iowrite32(chan->chip, DMAC_CHSUSPREG, val); } @@ -1190,12 +1191,13 @@ static inline void axi_chan_resume(struct axi_dma_chan *chan) { u32 val; - val = axi_dma_ioread32(chan->chip, DMAC_CHEN); if (chan->chip->dw->hdata->reg_map_8_channels) { + val = axi_dma_ioread32(chan->chip, DMAC_CHEN); val &= ~(BIT(chan->id) << DMAC_CHAN_SUSP_SHIFT); val |= (BIT(chan->id) << DMAC_CHAN_SUSP_WE_SHIFT); axi_dma_iowrite32(chan->chip, DMAC_CHEN, val); } else { + val = axi_dma_ioread32(chan->chip, DMAC_CHSUSPREG); val &= ~(BIT(chan->id) << DMAC_CHAN_SUSP2_SHIFT); val |= (BIT(chan->id) << DMAC_CHAN_SUSP2_WE_SHIFT); axi_dma_iowrite32(chan->chip, DMAC_CHSUSPREG, val); From 866150431f5359964c68b003a1d178ab8cee01c5 Mon Sep 17 00:00:00 2001 From: Samin Guo Date: Wed, 17 Nov 2021 14:50:45 +0800 Subject: [PATCH 56/80] dmaengine: dw-axi-dmac: Handle xfer start while non-idle Signed-off-by: Samin Guo Signed-off-by: Curry Zhang --- drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c | 13 ++++++++++++- drivers/dma/dw-axi-dmac/dw-axi-dmac.h | 1 + 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c index 4743fc8bdfc15..7c7b9f2a5b3b5 100644 --- a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c +++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c @@ -377,11 +377,13 @@ static void axi_chan_block_xfer_start(struct axi_dma_chan *chan, u32 irq_mask; u8 lms = 0; /* Select AXI0 master for LLI fetching */ + chan->is_err = false; if (unlikely(axi_chan_is_hw_enable(chan))) { dev_err(chan2dev(chan), "%s is non-idle!\n", axi_chan_name(chan)); - return; + axi_chan_disable(chan); + chan->is_err = true; } axi_dma_enable(chan->chip); @@ -1013,6 +1015,14 @@ static noinline void axi_chan_handle_err(struct axi_dma_chan *chan, u32 status) /* The bad descriptor currently is in the head of vc list */ vd = vchan_next_desc(&chan->vc); + if (chan->is_err) { + struct axi_dma_desc *desc = vd_to_axi_desc(vd); + + axi_chan_block_xfer_start(chan, desc); + chan->is_err = false; + goto out; + } + /* Remove the completed descriptor from issued list */ list_del(&vd->node); @@ -1027,6 +1037,7 @@ static noinline void axi_chan_handle_err(struct axi_dma_chan *chan, u32 status) /* Try to restart the controller */ axi_chan_start_first_queued(chan); +out: spin_unlock_irqrestore(&chan->vc.lock, flags); } diff --git a/drivers/dma/dw-axi-dmac/dw-axi-dmac.h b/drivers/dma/dw-axi-dmac/dw-axi-dmac.h index be69a0b76860d..afb6be92140ae 100644 --- a/drivers/dma/dw-axi-dmac/dw-axi-dmac.h +++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac.h @@ -49,6 +49,7 @@ struct axi_dma_chan { struct dma_slave_config config; enum dma_transfer_direction direction; bool cyclic; + bool is_err; /* these other elements are all protected by vc.lock */ bool is_paused; }; From fc78080f6205c26492fa7512cf347f233f60b5fb Mon Sep 17 00:00:00 2001 From: Samin Guo Date: Wed, 17 Nov 2021 14:50:45 +0800 Subject: [PATCH 57/80] dmaengine: dw-axi-dmac: Add StarFive JH7100 support Signed-off-by: Samin Guo Signed-off-by: Emil Renner Berthing --- drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c | 11 ++++++++++- drivers/dma/dw-axi-dmac/dw-axi-dmac.h | 4 ++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c index 7c7b9f2a5b3b5..ce9511444bbba 100644 --- a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c +++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c @@ -86,7 +86,7 @@ static inline void axi_chan_config_write(struct axi_dma_chan *chan, cfg_lo = (config->dst_multblk_type << CH_CFG_L_DST_MULTBLK_TYPE_POS | config->src_multblk_type << CH_CFG_L_SRC_MULTBLK_TYPE_POS); - if (chan->chip->dw->hdata->reg_map_8_channels) { + if (!IS_ENABLED(CONFIG_SOC_STARFIVE) && chan->chip->dw->hdata->reg_map_8_channels) { cfg_hi = config->tt_fc << CH_CFG_H_TT_FC_POS | config->hs_sel_src << CH_CFG_H_HS_SEL_SRC_POS | config->hs_sel_dst << CH_CFG_H_HS_SEL_DST_POS | @@ -672,8 +672,13 @@ static int dw_axi_dma_set_hw_desc(struct axi_dma_chan *chan, hw_desc->lli->block_ts_lo = cpu_to_le32(block_ts - 1); +#ifdef CONFIG_SOC_STARFIVE + ctllo |= DWAXIDMAC_BURST_TRANS_LEN_16 << CH_CTL_L_DST_MSIZE_POS | + DWAXIDMAC_BURST_TRANS_LEN_16 << CH_CTL_L_SRC_MSIZE_POS; +#else ctllo |= DWAXIDMAC_BURST_TRANS_LEN_4 << CH_CTL_L_DST_MSIZE_POS | DWAXIDMAC_BURST_TRANS_LEN_4 << CH_CTL_L_SRC_MSIZE_POS; +#endif hw_desc->lli->ctl_lo = cpu_to_le32(ctllo); set_desc_src_master(hw_desc); @@ -1473,7 +1478,11 @@ static int dw_probe(struct platform_device *pdev) * Therefore, set constraint to 1024 * 4. */ dw->dma.dev->dma_parms = &dw->dma_parms; +#ifdef CONFIG_SOC_STARFIVE + dma_set_max_seg_size(&pdev->dev, DMAC_MAX_BLK_SIZE); +#else dma_set_max_seg_size(&pdev->dev, MAX_BLOCK_SIZE); +#endif platform_set_drvdata(pdev, chip); pm_runtime_enable(chip->dev); diff --git a/drivers/dma/dw-axi-dmac/dw-axi-dmac.h b/drivers/dma/dw-axi-dmac/dw-axi-dmac.h index afb6be92140ae..8530e3130b764 100644 --- a/drivers/dma/dw-axi-dmac/dw-axi-dmac.h +++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac.h @@ -282,7 +282,11 @@ enum { #define CH_CTL_L_SRC_MAST BIT(0) /* CH_CFG_H */ +#ifdef CONFIG_SOC_STARFIVE +#define CH_CFG_H_PRIORITY_POS 15 +#else #define CH_CFG_H_PRIORITY_POS 17 +#endif #define CH_CFG_H_DST_PER_POS 12 #define CH_CFG_H_SRC_PER_POS 7 #define CH_CFG_H_HS_SEL_DST_POS 4 From 3550e6eb0e0925aed42d92b3bf04deffd0df6628 Mon Sep 17 00:00:00 2001 From: Tom Date: Fri, 8 Jan 2021 02:57:50 +0800 Subject: [PATCH 58/80] dmaengine: Add dw-axi-dmac-starfive driver for JH7100 --- drivers/dma/Kconfig | 7 + drivers/dma/Makefile | 1 + drivers/dma/dw-axi-dmac-starfive/Makefile | 2 + .../dw-axi-dmac-starfive-misc.c | 323 ++++++++++++++++++ .../starfive_dmaengine_memcpy.c | 288 ++++++++++++++++ include/soc/starfive/jh7100_dma.h | 31 ++ 6 files changed, 652 insertions(+) create mode 100644 drivers/dma/dw-axi-dmac-starfive/Makefile create mode 100644 drivers/dma/dw-axi-dmac-starfive/dw-axi-dmac-starfive-misc.c create mode 100644 drivers/dma/dw-axi-dmac-starfive/starfive_dmaengine_memcpy.c create mode 100644 include/soc/starfive/jh7100_dma.h diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 6bcdb4e6a0d1d..30a25ab15f036 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -181,6 +181,13 @@ config DW_AXI_DMAC NOTE: This driver wasn't tested on 64 bit platform because of lack 64 bit platform with Synopsys DW AXI DMAC. +config DW_AXI_DMAC_STARFIVE + tristate "Synopsys DesignWare AXI DMA support for StarFive SOC" + depends on SOC_STARFIVE + help + Enable support for Synopsys DesignWare AXI DMA controller. + NOTE: It's for StarFive SOC. + config EP93XX_DMA bool "Cirrus Logic EP93xx DMA support" depends on ARCH_EP93XX || COMPILE_TEST diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index 616d926cf2a5f..5d6c354aae296 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_DMA_SA11X0) += sa11x0-dma.o obj-$(CONFIG_DMA_SUN4I) += sun4i-dma.o obj-$(CONFIG_DMA_SUN6I) += sun6i-dma.o obj-$(CONFIG_DW_AXI_DMAC) += dw-axi-dmac/ +obj-$(CONFIG_DW_AXI_DMAC_STARFIVE) += dw-axi-dmac-starfive/ obj-$(CONFIG_DW_DMAC_CORE) += dw/ obj-$(CONFIG_DW_EDMA) += dw-edma/ obj-$(CONFIG_EP93XX_DMA) += ep93xx_dma.o diff --git a/drivers/dma/dw-axi-dmac-starfive/Makefile b/drivers/dma/dw-axi-dmac-starfive/Makefile new file mode 100644 index 0000000000000..c30fd928982f9 --- /dev/null +++ b/drivers/dma/dw-axi-dmac-starfive/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_DW_AXI_DMAC_STARFIVE) += starfive_dmaengine_memcpy.o dw-axi-dmac-starfive-misc.o \ No newline at end of file diff --git a/drivers/dma/dw-axi-dmac-starfive/dw-axi-dmac-starfive-misc.c b/drivers/dma/dw-axi-dmac-starfive/dw-axi-dmac-starfive-misc.c new file mode 100644 index 0000000000000..edb26c66c7668 --- /dev/null +++ b/drivers/dma/dw-axi-dmac-starfive/dw-axi-dmac-starfive-misc.c @@ -0,0 +1,323 @@ +/* + * Copyright 2020 StarFive, Inc + * + * DW AXI dma driver for StarFive SoC VIC7100. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define DRIVER_NAME "dwaxidma" +#define AXIDMA_IOC_MAGIC 'A' +#define AXIDMA_IOCGETCHN _IO(AXIDMA_IOC_MAGIC, 0) +#define AXIDMA_IOCCFGANDSTART _IO(AXIDMA_IOC_MAGIC, 1) +#define AXIDMA_IOCGETSTATUS _IO(AXIDMA_IOC_MAGIC, 2) +#define AXIDMA_IOCRELEASECHN _IO(AXIDMA_IOC_MAGIC, 3) + +#define AXI_DMA_MAX_CHANS 20 + +#define DMA_CHN_UNUSED 0 +#define DMA_CHN_USED 1 +#define DMA_STATUS_UNFINISHED 0 +#define DMA_STATUS_FINISHED 1 + +/* for DEBUG*/ +//#define DW_DMA_CHECK_RESULTS +//#define DW_DMA_PRINT_MEM +//#define DW_DMA_FLUSH_DESC + +struct axidma_chncfg { + unsigned long src_addr; /*dma addr*/ + unsigned long dst_addr; /*dma addr*/ + unsigned long virt_src; /*mmap src addr*/ + unsigned long virt_dst; /*mmap dst addr*/ + unsigned long phys; /*desc phys addr*/ + unsigned int len; /*transport lenth*/ + int mem_fd; /*fd*/ + unsigned char chn_num; /*dma channels number*/ + unsigned char status; /*dma transport status*/ +}; + +struct axidma_chns { + struct dma_chan *dma_chan; + unsigned char used; + unsigned char status; + unsigned char reserve[2]; +}; + +struct axidma_chns channels[AXI_DMA_MAX_CHANS]; +#ifdef DW_DMA_PRINT_MEM +void print_in_line_u64(u8 *p_name, u64 *p_buf, u32 len) +{ + u32 i, j; + u32 line; + u32* ptmp; + u32 len_tmp; + u32 rest = len / 4; + + printk("%s: 0x%#llx, 0x%x\n", + p_name, dw_virt_to_phys((void *)p_buf), len); + + if(len >= 0x1000) + len_tmp = 0x1000 / 32; //print 128 size of memory. + else + len_tmp = len / 8; //print real 100% size of memory. + + rest = len / 4; //one line print 8 u32 + + for (i = 0; i < len_tmp; i += 4, rest -= line) { + if (!(i % 4)) + printk(KERN_CONT KERN_INFO" %#llx: ", + dw_virt_to_phys((void *)(p_buf + i))); + + ptmp = (u32*)(p_buf + i); + line = (rest > 8) ? 8 : rest; + + for (j = 0; j < line; j++) + printk(KERN_CONT KERN_INFO "%08x ", *(ptmp + j)); + + printk(KERN_CONT KERN_INFO"\n"); + } +} +#endif + +static int axidma_open(struct inode *inode, struct file *file) +{ + /*Open: do nothing*/ + return 0; +} + +static int axidma_release(struct inode *inode, struct file *file) +{ + /* Release: do nothing */ + return 0; +} + +static ssize_t axidma_write(struct file *file, const char __user *data, + size_t len, loff_t *ppos) +{ + /* Write: do nothing */ + return 0; +} + +static void dma_complete_func(void *status) +{ + *(char *)status = DMA_STATUS_FINISHED; +} + +static long axidma_unlocked_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int i, ret; + dma_cap_mask_t mask; + dma_cookie_t cookie; + struct dma_device *dma_dev; + struct axidma_chncfg chncfg; + struct dma_async_tx_descriptor *tx; + +#ifdef DW_DMA_FLUSH_DESC + void *des_chncfg = &chncfg; + chncfg.phys = dw_virt_to_phys(des_chncfg); +#endif + memset(&chncfg, 0, sizeof(struct axidma_chncfg)); + + switch(cmd) { + case AXIDMA_IOCGETCHN: + for(i = 0; i < AXI_DMA_MAX_CHANS; i++) { + if(DMA_CHN_UNUSED == channels[i].used) + break; + } + if(AXI_DMA_MAX_CHANS == i) { + printk("Get dma chn failed, because no idle channel\n"); + goto error; + } else { + channels[i].used = DMA_CHN_USED; + channels[i].status = DMA_STATUS_UNFINISHED; + chncfg.status = DMA_STATUS_UNFINISHED; + chncfg.chn_num = i; + } + dma_cap_zero(mask); + dma_cap_set(DMA_MEMCPY, mask); + channels[i].dma_chan = dma_request_channel(mask, NULL, NULL); + if(!channels[i].dma_chan) { + printk("dma request channel failed\n"); + channels[i].used = DMA_CHN_UNUSED; + goto error; + } + ret = copy_to_user((void __user *)arg, &chncfg, + sizeof(struct axidma_chncfg)); + if(ret) { + printk("Copy to user failed\n"); + goto error; + } + break; + case AXIDMA_IOCCFGANDSTART: +#ifdef DW_DMA_CHECK_RESULTS + void *src,*dst; +#endif + ret = copy_from_user(&chncfg, (void __user *)arg, + sizeof(struct axidma_chncfg)); + if(ret) { + printk("Copy from user failed\n"); + goto error; + } + + if((chncfg.chn_num >= AXI_DMA_MAX_CHANS) || + (!channels[chncfg.chn_num].dma_chan)) { + printk("chn_num[%d] is invalid\n", chncfg.chn_num); + goto error; + } + dma_dev = channels[chncfg.chn_num].dma_chan->device; +#ifdef DW_DMA_FLUSH_DESC + sifive_l2_flush64_range(chncfg.phys,sizeof(chncfg)); +#endif +#ifdef DW_DMA_CHECK_RESULTS + src = dw_phys_to_virt(chncfg.src_addr); + dst = dw_phys_to_virt(chncfg.dst_addr); +#endif + sifive_l2_flush64_range(chncfg.src_addr, chncfg.len); + + tx = dma_dev->device_prep_dma_memcpy( + channels[chncfg.chn_num].dma_chan, + chncfg.dst_addr, chncfg.src_addr, chncfg.len, + DMA_CTRL_ACK | DMA_PREP_INTERRUPT); + if(!tx){ + printk("Failed to prepare DMA memcpy\n"); + goto error; + } + channels[chncfg.chn_num].status = DMA_STATUS_UNFINISHED; + tx->callback_param = &channels[chncfg.chn_num].status; + tx->callback = dma_complete_func; + cookie = tx->tx_submit(tx); + if(dma_submit_error(cookie)) { + printk("Failed to dma tx_submit\n"); + goto error; + } + dma_async_issue_pending(channels[chncfg.chn_num].dma_chan); + /*flush dcache*/ + sifive_l2_flush64_range(chncfg.dst_addr, chncfg.len); +#ifdef DW_DMA_PRINT_MEM + print_in_line_u64((u8 *)"src", (u64 *)src, chncfg.len); + print_in_line_u64((u8 *)"dst", (u64 *)dst, chncfg.len); +#endif +#ifdef DW_DMA_CHECK_RESULTS + if(memcmp(src, dst, chncfg.len)) + printk("check data faild.\n"); + else + printk("check data ok.\n"); +#endif + break; + + case AXIDMA_IOCGETSTATUS: + ret = copy_from_user(&chncfg, (void __user *)arg, + sizeof(struct axidma_chncfg)); + if(ret) { + printk("Copy from user failed\n"); + goto error; + } + + if(chncfg.chn_num >= AXI_DMA_MAX_CHANS) { + printk("chn_num[%d] is invalid\n", chncfg.chn_num); + goto error; + } + + chncfg.status = channels[chncfg.chn_num].status; + + ret = copy_to_user((void __user *)arg, &chncfg, + sizeof(struct axidma_chncfg)); + if(ret) { + printk("Copy to user failed\n"); + goto error; + } + break; + + case AXIDMA_IOCRELEASECHN: + ret = copy_from_user(&chncfg, (void __user *)arg, + sizeof(struct axidma_chncfg)); + if(ret) { + printk("Copy from user failed\n"); + goto error; + } + + if((chncfg.chn_num >= AXI_DMA_MAX_CHANS) || + (!channels[chncfg.chn_num].dma_chan)) { + printk("chn_num[%d] is invalid\n", chncfg.chn_num); + goto error; + } + + dma_release_channel(channels[chncfg.chn_num].dma_chan); + channels[chncfg.chn_num].used = DMA_CHN_UNUSED; + channels[chncfg.chn_num].status = DMA_STATUS_UNFINISHED; + break; + + default: + printk("Don't support cmd [%d]\n", cmd); + break; + } + return 0; + +error: + return -EFAULT; +} + +/* + * Kernel Interfaces + */ +static struct file_operations axidma_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = axidma_write, + .unlocked_ioctl = axidma_unlocked_ioctl, + .open = axidma_open, + .release = axidma_release, +}; + +static struct miscdevice axidma_miscdev = { + .minor = MISC_DYNAMIC_MINOR, + .name = DRIVER_NAME, + .fops = &axidma_fops, +}; + +static int __init axidma_init(void) +{ + int ret = misc_register(&axidma_miscdev); + if(ret) { + printk (KERN_ERR "cannot register miscdev (err=%d)\n", ret); + return ret; + } + + memset(&channels, 0, sizeof(channels)); + + return 0; +} + +static void __exit axidma_exit(void) +{ + misc_deregister(&axidma_miscdev); +} + +module_init(axidma_init); +module_exit(axidma_exit); + +MODULE_AUTHOR("samin.guo"); +MODULE_DESCRIPTION("DW Axi Dmac Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/dma/dw-axi-dmac-starfive/starfive_dmaengine_memcpy.c b/drivers/dma/dw-axi-dmac-starfive/starfive_dmaengine_memcpy.c new file mode 100644 index 0000000000000..0b425c639dff9 --- /dev/null +++ b/drivers/dma/dw-axi-dmac-starfive/starfive_dmaengine_memcpy.c @@ -0,0 +1,288 @@ +/* + * Copyright 2020 StarFive, Inc + * + * API for dma mem2mem. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static volatile int dma_finished = 0; +static DECLARE_WAIT_QUEUE_HEAD(wq); + +u64 dw_virt_to_phys(void *vaddr) +{ + u64 pfn_offset = ((u64)vaddr) & 0xfff; + + return _dw_virt_to_phys((u64 *)vaddr) + pfn_offset; +} +EXPORT_SYMBOL(dw_virt_to_phys); + +void *dw_phys_to_virt(u64 phys) +{ + u64 pfn_offset = phys & 0xfff; + + return (void *)(_dw_phys_to_virt(phys) + pfn_offset); +} +EXPORT_SYMBOL(dw_phys_to_virt); + +static void tx_callback(void *dma_async_param) +{ + dma_finished = 1; + wake_up_interruptible(&wq); +} + +static int _dma_async_alloc_buf(struct device *dma_dev, + void **src, void **dst, size_t size, + dma_addr_t *src_dma, dma_addr_t *dst_dma) +{ + *src = dma_alloc_coherent(dma_dev, size, src_dma, GFP_KERNEL); + if(!(*src)) { + DMA_DEBUG("src alloc err.\n"); + goto _FAILED_ALLOC_SRC; + } + + *dst = dma_alloc_coherent(dma_dev, size, dst_dma, GFP_KERNEL); + if(!(*dst)) { + DMA_DEBUG("dst alloc err.\n"); + goto _FAILED_ALLOC_DST; + } + + return 0; + +_FAILED_ALLOC_DST: + dma_free_coherent(dma_dev, size, *src, *src_dma); + +_FAILED_ALLOC_SRC: + dma_free_coherent(dma_dev, size, *dst, *dst_dma); + + return -1; +} + +static int _dma_async_prebuf(void *src, void *dst, size_t size) +{ + memset((u8 *)src, 0xff, size); + memset((u8 *)dst, 0x00, size); + return 0; +} + +static int _dma_async_check_data(void *src, void *dst, size_t size) +{ + return memcmp(src, dst, size); +} + +static void _dma_async_release(struct dma_chan *chan) +{ + dma_release_channel(chan); +} + +static struct dma_chan *_dma_get_channel(enum dma_transaction_type tx_type) +{ + dma_cap_mask_t dma_mask; + + dma_cap_zero(dma_mask); + dma_cap_set(tx_type, dma_mask); + + return dma_request_channel(dma_mask, NULL, NULL); +} + +static struct dma_async_tx_descriptor *_dma_async_get_desc( + struct dma_chan *chan, + dma_addr_t src_dma, dma_addr_t dst_dma, + size_t size) +{ + dma_finished = 0; + return dmaengine_prep_dma_memcpy(chan, dst_dma, src_dma, size, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); +} + +static void _dma_async_do_start(struct dma_async_tx_descriptor *desc, + struct dma_chan *chan) +{ + dma_cookie_t dma_cookie = dmaengine_submit(desc); + if (dma_submit_error(dma_cookie)) + DMA_DEBUG("Failed to do DMA tx_submit\n"); + + dma_async_issue_pending(chan); + wait_event_interruptible(wq, dma_finished); +} + +int dw_dma_async_do_memcpy(void *src, void *dst, size_t size) +{ + int ret; + struct device *dma_dev; + struct dma_chan *chan; + dma_addr_t src_dma, dst_dma; + struct dma_async_tx_descriptor *desc; + + const struct iommu_ops *iommu; + u64 dma_addr = 0, dma_size = 0; + + dma_dev = kzalloc(sizeof(*dma_dev), GFP_KERNEL); + if(!dma_dev){ + dev_err(dma_dev, "kmalloc error.\n"); + return -ENOMEM; + } + + dma_dev->bus = NULL; + dma_dev->coherent_dma_mask = 0xffffffff; + + iort_dma_setup(dma_dev, &dma_addr, &dma_size); + iommu = iort_iommu_configure_id(dma_dev, NULL); + if (PTR_ERR(iommu) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + arch_setup_dma_ops(dma_dev, dst_dma, dma_size, iommu, true); + + if(_dma_async_alloc_buf(dma_dev, &src, &dst, size, &src_dma, &dst_dma)) { + dev_err(dma_dev, "Err alloc.\n"); + return -ENOMEM; + } + + DMA_DEBUG("src=%#llx, dst=%#llx\n", (u64)src, (u64)dst); + DMA_DEBUG("dma_src=%#x dma_dst=%#x\n", (u32)src_dma, (u32)dst_dma); + + _dma_async_prebuf(src, dst, size); + + chan = _dma_get_channel(DMA_MEMCPY); + if(!chan ){ + DMA_PRINTK("Err get chan.\n"); + return -EBUSY; + } + DMA_DEBUG("get chan ok.\n"); + + desc = _dma_async_get_desc(chan, src_dma, dst_dma, size); + if(!desc){ + DMA_PRINTK("Err get desc.\n"); + dma_release_channel(chan); + return -ENOMEM; + } + DMA_DEBUG("get desc ok.\n"); + + desc->callback = tx_callback; + + sifive_l2_flush64_range(src_dma, size); + sifive_l2_flush64_range(dst_dma, size); + + _dma_async_do_start(desc, chan); + _dma_async_release(chan); + + ret = _dma_async_check_data(src, dst, size); + + dma_free_coherent(dma_dev, size, src, src_dma); + dma_free_coherent(dma_dev, size, dst, dst_dma); + + return ret; +} +EXPORT_SYMBOL(dw_dma_async_do_memcpy); + +/* +* phys addr for dma. +*/ +int dw_dma_memcpy_raw(dma_addr_t src_dma, dma_addr_t dst_dma, size_t size) +{ + struct dma_chan *chan; + struct device *dma_dev; + struct dma_async_tx_descriptor *desc; + + const struct iommu_ops *iommu; + u64 dma_addr = 0, dma_size = 0; + + dma_dev = kzalloc(sizeof(*dma_dev), GFP_KERNEL); + if(!dma_dev){ + DMA_PRINTK("kmalloc error.\n"); + return -ENOMEM; + } + + dma_dev->bus = NULL; + dma_dev->coherent_dma_mask = 0xffffffff; + + iort_dma_setup(dma_dev, &dma_addr, &dma_size); + iommu = iort_iommu_configure_id(dma_dev, NULL); + if (PTR_ERR(iommu) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + arch_setup_dma_ops(dma_dev, dst_dma, dma_size, iommu, true); + + chan = _dma_get_channel(DMA_MEMCPY); + if(!chan){ + DMA_PRINTK("Error get chan.\n"); + return -EBUSY; + } + DMA_DEBUG("get chan ok.\n"); + + DMA_DEBUG("src_dma=%#llx, dst_dma=%#llx \n", src_dma, dst_dma); + desc = _dma_async_get_desc(chan, src_dma, dst_dma, size); + if(!desc){ + DMA_PRINTK("Error get desc.\n"); + dma_release_channel(chan); + return -ENOMEM; + } + DMA_DEBUG("get desc ok.\n"); + + desc->callback = tx_callback; + + sifive_l2_flush64_range(src_dma, size); + sifive_l2_flush64_range(dst_dma, size); + + _dma_async_do_start(desc, chan); + _dma_async_release(chan); + + return 0; +} +EXPORT_SYMBOL(dw_dma_memcpy_raw); + +/* +*virtl addr for cpu. +*/ +int dw_dma_memcpy(void *src, void *dst, size_t size) +{ + dma_addr_t src_dma, dst_dma; + + src_dma = dw_virt_to_phys(src); + dst_dma = dw_virt_to_phys(dst); + + dw_dma_memcpy_raw(src_dma, dst_dma, size); + return 0; +} +EXPORT_SYMBOL(dw_dma_memcpy); + +int dw_dma_mem2mem_test(void) +{ + int ret; + void *src = NULL; + void *dst = NULL; + size_t size = 256; + + ret = dw_dma_async_do_memcpy(src, dst, size); + if(ret){ + DMA_PRINTK("memcpy failed.\n"); + } else { + DMA_PRINTK("memcpy ok.\n"); + } + + return ret; +} diff --git a/include/soc/starfive/jh7100_dma.h b/include/soc/starfive/jh7100_dma.h new file mode 100644 index 0000000000000..5bdbf48ab9a28 --- /dev/null +++ b/include/soc/starfive/jh7100_dma.h @@ -0,0 +1,31 @@ +#ifndef STARFIVE_JH7100_DMA_H +#define STARFIVE_JH7100_DMA_H + +#include + +#define CONFIG_DW_DEBUG + +#define DMA_PRINTK(fmt,...) \ + printk("[DW_DMA] %s():%d \n" fmt, __func__, __LINE__, ##__VA_ARGS__) + +#ifdef CONFIG_DW_DEBUG +#define DMA_DEBUG(fmt,...) \ + printk("[DW_DMA_DEBUG] %s():%d \n" fmt, __func__, __LINE__, ##__VA_ARGS__) +#else +#define DMA_BEBUG(fmt,...) +#endif + +#define _dw_virt_to_phys(vaddr) (pfn_to_phys(virt_to_pfn(vaddr))) +#define _dw_phys_to_virt(paddr) (page_to_virt(phys_to_page(paddr))) + +void *dw_phys_to_virt(u64 phys); +u64 dw_virt_to_phys(void *vaddr); + +int dw_dma_async_do_memcpy(void *src, void *dst, size_t size); +int dw_dma_memcpy_raw(dma_addr_t src_dma, dma_addr_t dst_dma, size_t size); +int dw_dma_memcpy(void *src, void *dst, size_t size); + +int dw_dma_mem2mem_arry(void); +int dw_dma_mem2mem_test(void); + +#endif /* STARFIVE_JH7100_DMA_H */ From d6951bfc42f1e13a282877fe1afb1691284b7133 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 29 Jun 2021 16:04:44 +0200 Subject: [PATCH 59/80] dmaengine: dw-axi-dmac-starfive: Remove calls specific to ARM64 ACPI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit iort_dma_setup() is being removed by commit db59e1b6e49201be ("ACPI: arm64: Move DMA setup operations out of IORT") in iommu/next: drivers/dma/dw-axi-dmac-starfive/starfive_dmaengine_memcpy.c: In function ‘dw_dma_async_ do_memcpy’: drivers/dma/dw-axi-dmac-starfive/starfive_dmaengine_memcpy.c:152:2: error: implicit decl aration of function ‘iort_dma_setup’ [-Werror=implicit-function-declaration] 152 | iort_dma_setup(dma_dev, &dma_addr, &dma_size); | ^~~~~~~~~~~~~~ drivers/dma/dw-axi-dmac-starfive/starfive_dmaengine_memcpy.c:153:8: warning: assignment to ‘const struct iommu_ops *’ from ‘int’ makes pointer from integer without a cast [-Wint-conversion] 153 | iommu = iort_iommu_configure_id(dma_dev, NULL); | ^ drivers/dma/dw-axi-dmac-starfive/starfive_dmaengine_memcpy.c: In function ‘dw_dma_memcpy_raw’: drivers/dma/dw-axi-dmac-starfive/starfive_dmaengine_memcpy.c:223:8: warning: assignment to ‘const struct iommu_ops *’ from ‘int’ makes pointer from integer without a cast [-Wint-conversion] 223 | iommu = iort_iommu_configure_id(dma_dev, NULL); | ^ iort_dma_setup() and iort_iommu_configure_id() are part of the ARM64 ACPI implementation. As CONFIG_ACPI_IORT cannot be enabled on RISC-V, they were dummies anyway, so these calls can just be removed. [Emil: remove unused local variables too] Signed-off-by: Geert Uytterhoeven Signed-off-by: Emil Renner Berthing --- Boot-tested, but the affected code paths were not exercised. --- .../starfive_dmaengine_memcpy.c | 20 ++----------------- 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/drivers/dma/dw-axi-dmac-starfive/starfive_dmaengine_memcpy.c b/drivers/dma/dw-axi-dmac-starfive/starfive_dmaengine_memcpy.c index 0b425c639dff9..543c83fb3e74f 100644 --- a/drivers/dma/dw-axi-dmac-starfive/starfive_dmaengine_memcpy.c +++ b/drivers/dma/dw-axi-dmac-starfive/starfive_dmaengine_memcpy.c @@ -138,9 +138,6 @@ int dw_dma_async_do_memcpy(void *src, void *dst, size_t size) dma_addr_t src_dma, dst_dma; struct dma_async_tx_descriptor *desc; - const struct iommu_ops *iommu; - u64 dma_addr = 0, dma_size = 0; - dma_dev = kzalloc(sizeof(*dma_dev), GFP_KERNEL); if(!dma_dev){ dev_err(dma_dev, "kmalloc error.\n"); @@ -150,12 +147,7 @@ int dw_dma_async_do_memcpy(void *src, void *dst, size_t size) dma_dev->bus = NULL; dma_dev->coherent_dma_mask = 0xffffffff; - iort_dma_setup(dma_dev, &dma_addr, &dma_size); - iommu = iort_iommu_configure_id(dma_dev, NULL); - if (PTR_ERR(iommu) == -EPROBE_DEFER) - return -EPROBE_DEFER; - - arch_setup_dma_ops(dma_dev, dst_dma, dma_size, iommu, true); + arch_setup_dma_ops(dma_dev, dst_dma, 0, NULL, true); if(_dma_async_alloc_buf(dma_dev, &src, &dst, size, &src_dma, &dst_dma)) { dev_err(dma_dev, "Err alloc.\n"); @@ -208,9 +200,6 @@ int dw_dma_memcpy_raw(dma_addr_t src_dma, dma_addr_t dst_dma, size_t size) struct device *dma_dev; struct dma_async_tx_descriptor *desc; - const struct iommu_ops *iommu; - u64 dma_addr = 0, dma_size = 0; - dma_dev = kzalloc(sizeof(*dma_dev), GFP_KERNEL); if(!dma_dev){ DMA_PRINTK("kmalloc error.\n"); @@ -220,12 +209,7 @@ int dw_dma_memcpy_raw(dma_addr_t src_dma, dma_addr_t dst_dma, size_t size) dma_dev->bus = NULL; dma_dev->coherent_dma_mask = 0xffffffff; - iort_dma_setup(dma_dev, &dma_addr, &dma_size); - iommu = iort_iommu_configure_id(dma_dev, NULL); - if (PTR_ERR(iommu) == -EPROBE_DEFER) - return -EPROBE_DEFER; - - arch_setup_dma_ops(dma_dev, dst_dma, dma_size, iommu, true); + arch_setup_dma_ops(dma_dev, dst_dma, 0, NULL, true); chan = _dma_get_channel(DMA_MEMCPY); if(!chan){ From 8d4395b2c5f88969f597db41b368651dd4916268 Mon Sep 17 00:00:00 2001 From: Walker Chen Date: Wed, 17 Nov 2021 10:38:48 +0800 Subject: [PATCH 60/80] net: phy: motorcomm: Support the YT8521 gigabit PHY Signed-off-by: Walker Chen --- drivers/net/phy/Kconfig | 2 +- drivers/net/phy/motorcomm.c | 448 ++++++++++++++++++++++++++++++++++++ 2 files changed, 449 insertions(+), 1 deletion(-) diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 902495afcb381..8318013b17d96 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -245,7 +245,7 @@ config MOTORCOMM_PHY tristate "Motorcomm PHYs" help Enables support for Motorcomm network PHYs. - Currently supports the YT8511 gigabit PHY. + Currently supports the YT8511 and YT8521 gigabit PHYs. config NATIONAL_PHY tristate "National Semiconductor PHYs" diff --git a/drivers/net/phy/motorcomm.c b/drivers/net/phy/motorcomm.c index 7e6ac2c5e27ef..add0eeff43ce8 100644 --- a/drivers/net/phy/motorcomm.c +++ b/drivers/net/phy/motorcomm.c @@ -10,6 +10,8 @@ #include #define PHY_ID_YT8511 0x0000010a +#define PHY_ID_YT8521 0x0000011a +#define MOTORCOMM_PHY_ID_MASK 0x00000fff #define YT8511_PAGE_SELECT 0x1e #define YT8511_PAGE 0x1f @@ -38,6 +40,53 @@ #define YT8511_DELAY_FE_TX_EN (0xf << 12) #define YT8511_DELAY_FE_TX_DIS (0x2 << 12) +#define YT8521_SLEEP_SW_EN BIT(15) +#define YT8521_LINK_STATUS BIT(10) +#define YT8521_DUPLEX 0x2000 +#define YT8521_SPEED_MODE 0xc000 +#define YTPHY_REG_SPACE_UTP 0 +#define YTPHY_REG_SPACE_FIBER 2 +#define REG_PHY_SPEC_STATUS 0x11 +/* based on yt8521 wol config register */ +#define YTPHY_UTP_INTR_REG 0x12 + +#define SYS_WAKEUP_BASED_ON_ETH_PKT 0 + +/* to enable system WOL of phy, please define this macro to 1 + * otherwise, define it to 0. + */ +#define YTPHY_ENABLE_WOL 0 + +#if (YTPHY_ENABLE_WOL) + #undef SYS_WAKEUP_BASED_ON_ETH_PKT + #define SYS_WAKEUP_BASED_ON_ETH_PKT 1 +#endif + +#if (YTPHY_ENABLE_WOL) +enum ytphy_wol_type_e { + YTPHY_WOL_TYPE_LEVEL, + YTPHY_WOL_TYPE_PULSE, + YTPHY_WOL_TYPE_MAX +}; +typedef enum ytphy_wol_type_e ytphy_wol_type_t; + +enum ytphy_wol_width_e { + YTPHY_WOL_WIDTH_84MS, + YTPHY_WOL_WIDTH_168MS, + YTPHY_WOL_WIDTH_336MS, + YTPHY_WOL_WIDTH_672MS, + YTPHY_WOL_WIDTH_MAX +}; +typedef enum ytphy_wol_width_e ytphy_wol_width_t; + +struct ytphy_wol_cfg_s { + int enable; + int type; + int width; +}; +typedef struct ytphy_wol_cfg_s ytphy_wol_cfg_t; +#endif /*(YTPHY_ENABLE_WOL)*/ + static int yt8511_read_page(struct phy_device *phydev) { return __phy_read(phydev, YT8511_PAGE_SELECT); @@ -111,6 +160,386 @@ static int yt8511_config_init(struct phy_device *phydev) return phy_restore_page(phydev, oldpage, ret); } +int genphy_config_init(struct phy_device *phydev) +{ + return genphy_read_abilities(phydev); +} + +static int ytphy_read_ext(struct phy_device *phydev, u32 regnum) +{ + int ret; + int val; + + ret = phy_write(phydev, YT8511_PAGE_SELECT, regnum); + if (ret < 0) + return ret; + + val = phy_read(phydev, YT8511_PAGE); + + return val; +} + +static int ytphy_write_ext(struct phy_device *phydev, u32 regnum, u16 val) +{ + int ret; + + ret = phy_write(phydev, YT8511_PAGE_SELECT, regnum); + if (ret < 0) + return ret; + + ret = phy_write(phydev, YT8511_PAGE, val); + + return ret; +} + +int yt8521_soft_reset(struct phy_device *phydev) +{ + int ret, val; + + val = ytphy_read_ext(phydev, 0xa001); + ytphy_write_ext(phydev, 0xa001, (val & ~0x8000)); + + ret = genphy_soft_reset(phydev); + if (ret < 0) + return ret; + + return 0; +} + +#if (YTPHY_ENABLE_WOL) +static int ytphy_switch_reg_space(struct phy_device *phydev, int space) +{ + int ret; + + if (space == YTPHY_REG_SPACE_UTP) + ret = ytphy_write_ext(phydev, 0xa000, 0); + else + ret = ytphy_write_ext(phydev, 0xa000, 2); + + return ret; +} + +static int ytphy_wol_en_cfg(struct phy_device *phydev, ytphy_wol_cfg_t wol_cfg) +{ + int ret=0; + int val=0; + + val = ytphy_read_ext(phydev, YTPHY_WOL_CFG_REG); + if (val < 0) + return val; + + if(wol_cfg.enable) { + val |= YTPHY_WOL_CFG_EN; + + if(wol_cfg.type == YTPHY_WOL_TYPE_LEVEL) { + val &= ~YTPHY_WOL_CFG_TYPE; + val &= ~YTPHY_WOL_CFG_INTR_SEL; + } else if(wol_cfg.type == YTPHY_WOL_TYPE_PULSE) { + val |= YTPHY_WOL_CFG_TYPE; + val |= YTPHY_WOL_CFG_INTR_SEL; + + if(wol_cfg.width == YTPHY_WOL_WIDTH_84MS) { + val &= ~YTPHY_WOL_CFG_WIDTH1; + val &= ~YTPHY_WOL_CFG_WIDTH2; + } else if(wol_cfg.width == YTPHY_WOL_WIDTH_168MS) { + val |= YTPHY_WOL_CFG_WIDTH1; + val &= ~YTPHY_WOL_CFG_WIDTH2; + } else if(wol_cfg.width == YTPHY_WOL_WIDTH_336MS) { + val &= ~YTPHY_WOL_CFG_WIDTH1; + val |= YTPHY_WOL_CFG_WIDTH2; + } else if(wol_cfg.width == YTPHY_WOL_WIDTH_672MS) { + val |= YTPHY_WOL_CFG_WIDTH1; + val |= YTPHY_WOL_CFG_WIDTH2; + } + } + } else { + val &= ~YTPHY_WOL_CFG_EN; + val &= ~YTPHY_WOL_CFG_INTR_SEL; + } + + ret = ytphy_write_ext(phydev, YTPHY_WOL_CFG_REG, val); + if (ret < 0) + return ret; + + return 0; +} + +static void ytphy_get_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol) +{ + int val = 0; + + wol->supported = WAKE_MAGIC; + wol->wolopts = 0; + + val = ytphy_read_ext(phydev, YTPHY_WOL_CFG_REG); + if (val < 0) + return; + + if (val & YTPHY_WOL_CFG_EN) + wol->wolopts |= WAKE_MAGIC; + + return; +} + +static int ytphy_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol) +{ + int ret, pre_page, val; + ytphy_wol_cfg_t wol_cfg; + struct net_device *p_attached_dev = phydev->attached_dev; + + memset(&wol_cfg,0,sizeof(ytphy_wol_cfg_t)); + pre_page = ytphy_read_ext(phydev, 0xa000); + if (pre_page < 0) + return pre_page; + + /* Switch to phy UTP page */ + ret = ytphy_switch_reg_space(phydev, YTPHY_REG_SPACE_UTP); + if (ret < 0) + return ret; + + if (wol->wolopts & WAKE_MAGIC) { + /* Enable the WOL interrupt */ + val = phy_read(phydev, YTPHY_UTP_INTR_REG); + val |= YTPHY_WOL_INTR; + ret = phy_write(phydev, YTPHY_UTP_INTR_REG, val); + if (ret < 0) + return ret; + + /* Set the WOL config */ + wol_cfg.enable = 1; //enable + wol_cfg.type= YTPHY_WOL_TYPE_PULSE; + wol_cfg.width= YTPHY_WOL_WIDTH_672MS; + ret = ytphy_wol_en_cfg(phydev, wol_cfg); + if (ret < 0) + return ret; + + /* Store the device address for the magic packet */ + ret = ytphy_write_ext(phydev, YTPHY_MAGIC_PACKET_MAC_ADDR2, + ((p_attached_dev->dev_addr[0] << 8) | + p_attached_dev->dev_addr[1])); + if (ret < 0) + return ret; + ret = ytphy_write_ext(phydev, YTPHY_MAGIC_PACKET_MAC_ADDR1, + ((p_attached_dev->dev_addr[2] << 8) | + p_attached_dev->dev_addr[3])); + if (ret < 0) + return ret; + ret = ytphy_write_ext(phydev, YTPHY_MAGIC_PACKET_MAC_ADDR0, + ((p_attached_dev->dev_addr[4] << 8) | + p_attached_dev->dev_addr[5])); + if (ret < 0) + return ret; + } else { + wol_cfg.enable = 0; //disable + wol_cfg.type= YTPHY_WOL_TYPE_MAX; + wol_cfg.width= YTPHY_WOL_WIDTH_MAX; + ret = ytphy_wol_en_cfg(phydev, wol_cfg); + if (ret < 0) + return ret; + } + + /* Recover to previous register space page */ + ret = ytphy_switch_reg_space(phydev, pre_page); + if (ret < 0) + return ret; + + return 0; +} +#endif /*(YTPHY_ENABLE_WOL)*/ + +static int yt8521_config_init(struct phy_device *phydev) +{ + int ret; + int val; + + phydev->irq = PHY_POLL; + + ytphy_write_ext(phydev, 0xa000, 0); + + ret = genphy_config_init(phydev); + if (ret < 0) + return ret; + + /* disable auto sleep */ + val = ytphy_read_ext(phydev, YT8511_EXT_SLEEP_CTRL); + if (val < 0) + return val; + + val &= ~YT8521_SLEEP_SW_EN; + + ret = ytphy_write_ext(phydev, YT8511_EXT_SLEEP_CTRL, val); + if (ret < 0) + return ret; + + /* enable tx delay 450ps per step */ + val = ytphy_read_ext(phydev, 0xa003); + if (val < 0) { + printk(KERN_INFO "yt8521_config: read 0xa003 error!\n"); + return val; + } + val |= 0x3; + ret = ytphy_write_ext(phydev, 0xa003, val); + if (ret < 0) { + printk(KERN_INFO "yt8521_config: set 0xa003 error!\n"); + return ret; + } + + /* disable rx delay */ + val = ytphy_read_ext(phydev, 0xa001); + if (val < 0) { + printk(KERN_INFO "yt8521_config: read 0xa001 error!\n"); + return val; + } + val &= ~(1<<8); + ret = ytphy_write_ext(phydev, 0xa001, val); + if (ret < 0) { + printk(KERN_INFO "yt8521_config: failed to disable rx_delay!\n"); + return ret; + } + + /* enable RXC clock when no wire plug */ + ret = ytphy_write_ext(phydev, 0xa000, 0); + if (ret < 0) + return ret; + + val = ytphy_read_ext(phydev, YT8511_EXT_CLK_GATE); + if (val < 0) + return val; + val &= ~(1 << 12); + ret = ytphy_write_ext(phydev, YT8511_EXT_CLK_GATE, val); + if (ret < 0) + return ret; + + return ret; +} + +/* + * for fiber mode, there is no 10M speed mode and + * this function is for this purpose. + */ +static int yt8521_adjust_status(struct phy_device *phydev, int val, int is_utp) +{ + int speed_mode, duplex; + int speed = SPEED_UNKNOWN; + + duplex = (val & YT8521_DUPLEX) >> 13; + speed_mode = (val & YT8521_SPEED_MODE) >> 14; + switch (speed_mode) { + case 0: + if (is_utp) + speed = SPEED_10; + break; + case 1: + speed = SPEED_100; + break; + case 2: + speed = SPEED_1000; + break; + case 3: + break; + default: + speed = SPEED_UNKNOWN; + break; + } + + phydev->speed = speed; + phydev->duplex = duplex; + return 0; +} + +static int yt8521_read_status(struct phy_device *phydev) +{ + int ret; + volatile int val; + volatile int link; + int link_utp = 0; + + /* reading UTP */ + ret = ytphy_write_ext(phydev, 0xa000, 0); + if (ret < 0) + return ret; + + val = phy_read(phydev, REG_PHY_SPEC_STATUS); + if (val < 0) + return val; + + link = val & YT8521_LINK_STATUS; + if (link) { + link_utp = 1; + yt8521_adjust_status(phydev, val, 1); + } else { + link_utp = 0; + } + + if (link_utp) { + phydev->link = 1; + ytphy_write_ext(phydev, 0xa000, 0); + } else { + phydev->link = 0; + } + + return 0; +} + +int yt8521_suspend(struct phy_device *phydev) +{ +#if !(SYS_WAKEUP_BASED_ON_ETH_PKT) + int value; + + ytphy_write_ext(phydev, 0xa000, 0); + value = phy_read(phydev, MII_BMCR); + phy_write(phydev, MII_BMCR, value | BMCR_PDOWN); + + ytphy_write_ext(phydev, 0xa000, 2); + value = phy_read(phydev, MII_BMCR); + phy_write(phydev, MII_BMCR, value | BMCR_PDOWN); + + ytphy_write_ext(phydev, 0xa000, 0); +#endif /*!(SYS_WAKEUP_BASED_ON_ETH_PKT)*/ + + return 0; +} + +int yt8521_resume(struct phy_device *phydev) +{ +#if !(SYS_WAKEUP_BASED_ON_ETH_PKT) + int value; + int ret; + + ytphy_write_ext(phydev, 0xa000, 0); + value = phy_read(phydev, MII_BMCR); + phy_write(phydev, MII_BMCR, value & ~BMCR_PDOWN); + + /* disable auto sleep */ + value = ytphy_read_ext(phydev, YT8511_EXT_SLEEP_CTRL); + if (value < 0) + return value; + + value &= ~YT8521_SLEEP_SW_EN; + ret = ytphy_write_ext(phydev, YT8511_EXT_SLEEP_CTRL, value); + if (ret < 0) + return ret; + + /* enable RXC clock when no wire plug */ + value = ytphy_read_ext(phydev, YT8511_EXT_CLK_GATE); + if (value < 0) + return value; + value &= ~(1 << 12); + ret = ytphy_write_ext(phydev, YT8511_EXT_CLK_GATE, value); + if (ret < 0) + return ret; + + ytphy_write_ext(phydev, 0xa000, 2); + value = phy_read(phydev, MII_BMCR); + phy_write(phydev, MII_BMCR, value & ~BMCR_PDOWN); + + ytphy_write_ext(phydev, 0xa000, 0); + +#endif /*!(SYS_WAKEUP_BASED_ON_ETH_PKT)*/ + + return 0; +} + static struct phy_driver motorcomm_phy_drvs[] = { { PHY_ID_MATCH_EXACT(PHY_ID_YT8511), @@ -121,16 +550,35 @@ static struct phy_driver motorcomm_phy_drvs[] = { .read_page = yt8511_read_page, .write_page = yt8511_write_page, }, + { + PHY_ID_MATCH_EXACT(PHY_ID_YT8521), + .name = "YT8521 Gigabit Ethernet", + .phy_id_mask = MOTORCOMM_PHY_ID_MASK, + .flags = PHY_POLL, + .soft_reset = yt8521_soft_reset, + .config_aneg = genphy_config_aneg, + .aneg_done = genphy_aneg_done, + .config_init = yt8521_config_init, + .read_status = yt8521_read_status, + .suspend = yt8521_suspend, + .resume = yt8521_resume, +#if (YTPHY_ENABLE_WOL) + .get_wol = &ytphy_get_wol, + .set_wol = &ytphy_set_wol, +#endif + }, }; module_phy_driver(motorcomm_phy_drvs); MODULE_DESCRIPTION("Motorcomm PHY driver"); MODULE_AUTHOR("Peter Geis"); +MODULE_AUTHOR("Walker Chen "); MODULE_LICENSE("GPL"); static const struct mdio_device_id __maybe_unused motorcomm_tbl[] = { { PHY_ID_MATCH_EXACT(PHY_ID_YT8511) }, + { PHY_ID_MATCH_EXACT(PHY_ID_YT8521) }, { /* sentinal */ } }; From d84397a07feb98124d76160bc126fd9d534c316b Mon Sep 17 00:00:00 2001 From: Tom Date: Tue, 6 Apr 2021 13:30:26 +0800 Subject: [PATCH 61/80] net: stmmac: Configure gtxclk based on speed --- .../ethernet/stmicro/stmmac/dwmac-generic.c | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-generic.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-generic.c index 5e731a72cce81..b38d3aae496e5 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-generic.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-generic.c @@ -16,6 +16,50 @@ #include "stmmac.h" #include "stmmac_platform.h" +/* + * GMAC_GTXCLK 为 gmac 的时钟分频寄存器,低8位为分频值 + * bit name access default descript + * [31] clk_gmac_gtxclk enable RW 0x0 "1:enable; 0:disable" + * [30] reserved - 0x0 reserved + * [29:8] reserved - 0x0 reserved + * [7:0] clk_gmac_gtxclk divide ratio RW 0x4 divide value + * + * gmac 的 root 时钟为500M, gtxclk 需求的时钟如下: + * 1000M: gtxclk为125M,分频值为500/125 = 0x4 + * 100M: gtxclk为25M, 分频值为500/25 = 0x14 + * 10M: gtxclk为2.5M,分频值为500/2.5 = 0xc8 + */ +#ifdef CONFIG_SOC_STARFIVE +#define CLKGEN_BASE 0x11800000 +#define CLKGEN_GMAC_GTXCLK_OFFSET 0x1EC +#define CLKGEN_GMAC_GTXCLK_ADDR (CLKGEN_BASE + CLKGEN_GMAC_GTXCLK_OFFSET) + +#define CLKGEN_125M_DIV 0x4 +#define CLKGEN_25M_DIV 0x14 +#define CLKGEN_2_5M_DIV 0xc8 + +static void dwmac_fixed_speed(void *priv, unsigned int speed) +{ + u32 value; + void *addr = ioremap(CLKGEN_GMAC_GTXCLK_ADDR, sizeof(value)); + if (!addr) { + pr_err("%s can't remap CLKGEN_GMAC_GTXCLK_ADDR\n", __func__); + return; + } + + value = readl(addr) & (~0x000000FF); + + switch (speed) { + case SPEED_1000: value |= CLKGEN_125M_DIV; break; + case SPEED_100: value |= CLKGEN_25M_DIV; break; + case SPEED_10: value |= CLKGEN_2_5M_DIV; break; + default: iounmap(addr); return; + } + writel(value, addr); /*set gmac gtxclk*/ + iounmap(addr); +} +#endif + static int dwmac_generic_probe(struct platform_device *pdev) { struct plat_stmmacenet_data *plat_dat; @@ -52,6 +96,9 @@ static int dwmac_generic_probe(struct platform_device *pdev) if (ret) goto err_remove_config_dt; } +#ifdef CONFIG_SOC_STARFIVE + plat_dat->fix_mac_speed = dwmac_fixed_speed; +#endif ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); if (ret) From 9ed64a0ffcd69e7e3bc487cc44c93a460ed3027b Mon Sep 17 00:00:00 2001 From: Matteo Croce Date: Fri, 21 May 2021 03:26:38 +0200 Subject: [PATCH 62/80] net: stmmac: use GFP_DMA32 Signed-off-by: Matteo Croce --- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 8ded4be08b001..795ae67c152e9 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1461,7 +1461,7 @@ static int stmmac_init_rx_buffers(struct stmmac_priv *priv, struct dma_desc *p, { struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; struct stmmac_rx_buffer *buf = &rx_q->buf_pool[i]; - gfp_t gfp = (GFP_ATOMIC | __GFP_NOWARN); + gfp_t gfp = (GFP_ATOMIC | __GFP_NOWARN | GFP_DMA32); if (priv->dma_cap.addr64 <= 32) gfp |= GFP_DMA32; @@ -4486,7 +4486,7 @@ static inline void stmmac_rx_refill(struct stmmac_priv *priv, u32 queue) struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; int dirty = stmmac_rx_dirty(priv, queue); unsigned int entry = rx_q->dirty_rx; - gfp_t gfp = (GFP_ATOMIC | __GFP_NOWARN); + gfp_t gfp = (GFP_ATOMIC | __GFP_NOWARN | GFP_DMA32); if (priv->dma_cap.addr64 <= 32) gfp |= GFP_DMA32; From ebcaa4f28dee18dbf0505de624c0343ab5c5358a Mon Sep 17 00:00:00 2001 From: Walker Chen Date: Wed, 17 Nov 2021 15:50:50 +0800 Subject: [PATCH 63/80] ASoC: starfive: Add StarFive JH7100 audio drivers Signed-off-by: Michael Yan Signed-off-by: Jenny Zhang Signed-off-by: Walker Chen Signed-off-by: Emil Renner Berthing --- sound/soc/Kconfig | 1 + sound/soc/Makefile | 1 + sound/soc/starfive/Kconfig | 55 + sound/soc/starfive/Makefile | 19 + sound/soc/starfive/pwmdac.h | 153 +++ sound/soc/starfive/starfive_i2svad.c | 1089 +++++++++++++++++ sound/soc/starfive/starfive_i2svad.h | 245 ++++ sound/soc/starfive/starfive_i2svad_pcm.c | 248 ++++ sound/soc/starfive/starfive_pdm.c | 361 ++++++ sound/soc/starfive/starfive_pdm.h | 43 + sound/soc/starfive/starfive_pwmdac.c | 901 ++++++++++++++ sound/soc/starfive/starfive_pwmdac_pcm.c | 234 ++++ .../starfive/starfive_pwmdac_transmitter.c | 82 ++ sound/soc/starfive/starfive_spdif.c | 383 ++++++ sound/soc/starfive/starfive_spdif.h | 154 +++ sound/soc/starfive/starfive_spdif_pcm.c | 288 +++++ 16 files changed, 4257 insertions(+) create mode 100644 sound/soc/starfive/Kconfig create mode 100644 sound/soc/starfive/Makefile create mode 100644 sound/soc/starfive/pwmdac.h create mode 100644 sound/soc/starfive/starfive_i2svad.c create mode 100644 sound/soc/starfive/starfive_i2svad.h create mode 100644 sound/soc/starfive/starfive_i2svad_pcm.c create mode 100644 sound/soc/starfive/starfive_pdm.c create mode 100644 sound/soc/starfive/starfive_pdm.h create mode 100644 sound/soc/starfive/starfive_pwmdac.c create mode 100644 sound/soc/starfive/starfive_pwmdac_pcm.c create mode 100644 sound/soc/starfive/starfive_pwmdac_transmitter.c create mode 100644 sound/soc/starfive/starfive_spdif.c create mode 100644 sound/soc/starfive/starfive_spdif.h create mode 100644 sound/soc/starfive/starfive_spdif_pcm.c diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 5dcf77af07afc..5d14cdc8a252f 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -83,6 +83,7 @@ source "sound/soc/sh/Kconfig" source "sound/soc/sof/Kconfig" source "sound/soc/spear/Kconfig" source "sound/soc/sprd/Kconfig" +source "sound/soc/starfive/Kconfig" source "sound/soc/sti/Kconfig" source "sound/soc/stm/Kconfig" source "sound/soc/sunxi/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index a7b37c06dc436..86cf072d036f6 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -49,6 +49,7 @@ obj-$(CONFIG_SND_SOC) += pxa/ obj-$(CONFIG_SND_SOC) += qcom/ obj-$(CONFIG_SND_SOC) += rockchip/ obj-$(CONFIG_SND_SOC) += samsung/ +obj-$(CONFIG_SND_SOC) += starfive/ obj-$(CONFIG_SND_SOC) += sh/ obj-$(CONFIG_SND_SOC) += sof/ obj-$(CONFIG_SND_SOC) += spear/ diff --git a/sound/soc/starfive/Kconfig b/sound/soc/starfive/Kconfig new file mode 100644 index 0000000000000..e133fa6ededcc --- /dev/null +++ b/sound/soc/starfive/Kconfig @@ -0,0 +1,55 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2021 StarFive Technology Co., Ltd. + +config SND_STARFIVE_SPDIF + tristate "starfive spdif" + select SND_SOC_GENERIC_DMAENGINE_PCM + select REGMAP_MMIO + help + Say Y or M if you want to add support for codecs attached to the + I2S interface on VIC vic_starlight board. You will also need to select + the drivers for the rest of VIC audio subsystem. + +config SND_STARFIVE_SPDIF_PCM + bool "PCM PIO extension for spdif driver" + depends on SND_STARFIVE_SPDIF + help + Say Y or N if you want to add a custom ALSA extension that registers + a PCM and uses PIO to transfer data. + +config SND_STARFIVE_PWMDAC + tristate "starfive pwmdac Device Driver" + select SND_SOC_GENERIC_DMAENGINE_PCM + help + Say Y or M if you want to add support for sf pwmdac driver. + +config SND_STARFIVE_PWMDAC_PCM + bool "PCM PIO extension for pwmdac driver" + depends on SND_STARFIVE_PWMDAC + help + Say Y or N if you want to add a custom ALSA extension that registers + a PCM and uses PIO to transfer data. + +config SND_STARFIVE_PDM + tristate "starfive pdm Device Driver" + select REGMAP_MMIO + help + Say Y or M if you want to add support for sf pdm driver. + +config SND_STARFIVE_I2SVAD + tristate "starfive I2SVAD Device Driver" + select SND_SOC_GENERIC_DMAENGINE_PCM + help + Say Y or M if you want to add support for I2SVAD driver for + starfive I2SVAD device. + +config SND_STARFIVE_I2SVAD_PCM + bool "PCM PIO extension for I2SVAD driver" + depends on SND_STARFIVE_I2SVAD + 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 I2SVAD devices that don't have + DMA support. + diff --git a/sound/soc/starfive/Makefile b/sound/soc/starfive/Makefile new file mode 100644 index 0000000000000..a7c3b9f64aac7 --- /dev/null +++ b/sound/soc/starfive/Makefile @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Copyright (C) 2021 StarFive Technology Co., Ltd. +# +obj-$(CONFIG_SND_STARFIVE_SPDIF) += spdif.o + +spdif-y := starfive_spdif.o +spdif-$(CONFIG_SND_STARFIVE_SPDIF_PCM) += starfive_spdif_pcm.o + +obj-$(CONFIG_SND_STARFIVE_PWMDAC) += pwmdac.o + +pwmdac-y := starfive_pwmdac.o starfive_pwmdac_transmitter.o +pwmdac-$(CONFIG_SND_STARFIVE_PWMDAC_PCM) += starfive_pwmdac_pcm.o + +obj-$(CONFIG_SND_STARFIVE_PDM) += starfive_pdm.o + +obj-$(CONFIG_SND_STARFIVE_I2SVAD) += i2svad.o +i2svad-y := starfive_i2svad.o +i2svad-$(CONFIG_SND_STARFIVE_I2SVAD_PCM) += starfive_i2svad_pcm.o \ No newline at end of file diff --git a/sound/soc/starfive/pwmdac.h b/sound/soc/starfive/pwmdac.h new file mode 100644 index 0000000000000..8e5ceb1258c34 --- /dev/null +++ b/sound/soc/starfive/pwmdac.h @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 StarFive Technology Co., Ltd. + */ +#ifndef __STARFIVE_PWMDAC_LOCAL_H +#define __STARFIVE_PWMDAC_LOCAL_H + +#include +#include +#include +#include +#include +#include + +#define PWMDAC_WDATA 0 ///PWMDAC_BASE_ADDR +#define PWMDAC_CTRL 0x04 ///PWMDAC_BASE_ADDR + 0x04 +#define PWMDAC_SATAE 0x08 ///PWMDAC_BASE_ADDR + 0x08 +#define PWMDAC_RESERVED 0x0C ///PWMDAC_BASE_ADDR + 0x0C + +#define SFC_PWMDAC_SHIFT BIT(1) +#define SFC_PWMDAC_DUTY_CYCLE BIT(2) +#define SFC_PWMDAC_CNT_N BIT(4) + +#define SFC_PWMDAC_LEFT_RIGHT_DATA_CHANGE BIT(13) +#define SFC_PWMDAC_DATA_MODE BIT(14) + +#define FIFO_UN_FULL 0 +#define FIFO_FULL 1 + +enum pwmdac_lr_change{ + NO_CHANGE = 0, + CHANGE, +}; + +enum pwmdac_d_mode{ + UNSINGED_DATA = 0, + INVERTER_DATA_MSB, +}; + +enum pwmdac_shift_bit{ + PWMDAC_SHIFT_8 = 8, /*pwmdac shift 8 bit*/ + PWMDAC_SHIFT_10 = 10, /*pwmdac shift 10 bit*/ +}; + +enum pwmdac_duty_cycle{ + PWMDAC_CYCLE_LEFT = 0, /*pwmdac duty cycle left*/ + PWMDAC_CYCLE_RIGHT = 1, /*pwmdac duty cycle right*/ + PWMDAC_CYCLE_CENTER = 2, /*pwmdac duty cycle center*/ +}; + +/*sample count [12:4] <511*/ +enum pwmdac_sample_count{ + PWMDAC_SAMPLE_CNT_1 = 1, + PWMDAC_SAMPLE_CNT_2, + PWMDAC_SAMPLE_CNT_3, + PWMDAC_SAMPLE_CNT_4, + PWMDAC_SAMPLE_CNT_5, + PWMDAC_SAMPLE_CNT_6, + PWMDAC_SAMPLE_CNT_7, + PWMDAC_SAMPLE_CNT_8 = 1, //(32.468/8) == (12.288/3) == 4.096 + PWMDAC_SAMPLE_CNT_9, + PWMDAC_SAMPLE_CNT_10, + PWMDAC_SAMPLE_CNT_11, + PWMDAC_SAMPLE_CNT_12, + PWMDAC_SAMPLE_CNT_13, + PWMDAC_SAMPLE_CNT_14, + PWMDAC_SAMPLE_CNT_15, + PWMDAC_SAMPLE_CNT_16, + PWMDAC_SAMPLE_CNT_17, + PWMDAC_SAMPLE_CNT_18, + PWMDAC_SAMPLE_CNT_19, + PWMDAC_SAMPLE_CNT_20 = 20, + PWMDAC_SAMPLE_CNT_30 = 30, + PWMDAC_SAMPLE_CNT_511 = 511, +}; + + +enum data_shift{ + PWMDAC_DATA_LEFT_SHIFT_BIT_0 = 0, + PWMDAC_DATA_LEFT_SHIFT_BIT_1, + PWMDAC_DATA_LEFT_SHIFT_BIT_2, + PWMDAC_DATA_LEFT_SHIFT_BIT_3, + PWMDAC_DATA_LEFT_SHIFT_BIT_4, + PWMDAC_DATA_LEFT_SHIFT_BIT_5, + PWMDAC_DATA_LEFT_SHIFT_BIT_6, + PWMDAC_DATA_LEFT_SHIFT_BIT_7, + PWMDAC_DATA_LEFT_SHIFT_BIT_ALL, +}; + +enum pwmdac_config_list{ + shift_8Bit_unsigned = 0, + shift_8Bit_unsigned_dataShift, + shift_10Bit_unsigned, + shift_10Bit_unsigned_dataShift, + + shift_8Bit_inverter, + shift_8Bit_inverter_dataShift, + shift_10Bit_inverter, + shift_10Bit_inverter_dataShift, +}; + +struct sf_pwmdac_dev { + void __iomem *pwmdac_base; + resource_size_t mapbase; + u8 mode; + u8 shift_bit; + u8 duty_cycle; + u8 datan; + u8 data_mode; + u8 lr_change; + u8 shift; + u8 fifo_th; + bool use_pio; + spinlock_t lock; + int active; + + struct clk *clk_audio_root; + struct clk *clk_audio_src; + struct clk *clk_audio_12288; + struct clk *clk_dma1p_ahb; + struct clk *clk_pwmdac_apb; + struct clk *clk_dac_mclk; + struct reset_control *rst_apb_bus; + struct reset_control *rst_dma1p_ahb; + struct reset_control *rst_apb_pwmdac; + + struct device *dev; + struct snd_dmaengine_dai_dma_data play_dma_data; + struct snd_pcm_substream __rcu *tx_substream; + unsigned int (*tx_fn)(struct sf_pwmdac_dev *dev, + struct snd_pcm_runtime *runtime, unsigned int tx_ptr, + bool *period_elapsed); + unsigned int tx_ptr; + struct task_struct *tx_thread; + bool tx_thread_exit; +}; + + + +#if IS_ENABLED(CONFIG_SND_STARFIVE_PWMDAC_PCM) +void sf_pwmdac_pcm_push_tx(struct sf_pwmdac_dev *dev); +void sf_pwmdac_pcm_pop_rx(struct sf_pwmdac_dev *dev); +int sf_pwmdac_pcm_register(struct platform_device *pdev); +#else +void sf_pwmdac_pcm_push_tx(struct sf_pwmdac_dev *dev) { } +void sf_pwmdac_pcm_pop_rx(struct sf_pwmdac_dev *dev) { } +int sf_pwmdac_pcm_register(struct platform_device *pdev) +{ + return -EINVAL; +} +#endif + +#endif diff --git a/sound/soc/starfive/starfive_i2svad.c b/sound/soc/starfive/starfive_i2svad.c new file mode 100644 index 0000000000000..a5762ab3f85be --- /dev/null +++ b/sound/soc/starfive/starfive_i2svad.c @@ -0,0 +1,1089 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 StarFive Technology Co., Ltd. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "starfive_i2svad.h" +#include + + +/* vad control function*/ +static void vad_start(struct vad_params *vad) +{ + regmap_update_bits(vad->vad_map, VAD_MEM_SW, + VAD_MEM_SW_MASK, VAD_MEM_SW_TO_VAD); + regmap_update_bits(vad->vad_map, VAD_SW, + VAD_SW_MASK, VAD_SW_VAD_XMEM_ENABLE|VAD_SW_ADC_ENABLE); + regmap_update_bits(vad->vad_map, VAD_SPINT_EN, + VAD_SPINT_EN_MASK, VAD_SPINT_EN_ENABLE); + regmap_update_bits(vad->vad_map, VAD_SLINT_EN, + VAD_SLINT_EN_MASK, VAD_SLINT_EN_ENABLE); +} + +static void vad_stop(struct vad_params *vad) +{ + regmap_update_bits(vad->vad_map, VAD_SPINT_EN, + VAD_SPINT_EN_MASK, VAD_SLINT_EN_DISABLE); + regmap_update_bits(vad->vad_map, VAD_SLINT_EN, + VAD_SLINT_EN_MASK, VAD_SLINT_EN_DISABLE); + regmap_update_bits(vad->vad_map, VAD_SW, + VAD_SW_MASK, VAD_SW_VAD_XMEM_DISABLE|VAD_SW_ADC_DISABLE); + regmap_update_bits(vad->vad_map, VAD_MEM_SW, + VAD_MEM_SW_MASK, VAD_MEM_SW_TO_AXI); +} + +static void vad_status(struct vad_params *vad) +{ + u32 sp_value,sp_en; + u32 sl_value,sl_en; + + regmap_read(vad->vad_map, VAD_SPINT,&sp_value); + regmap_read(vad->vad_map, VAD_SPINT_EN,&sp_en); + if (sp_value&sp_en){ + regmap_update_bits(vad->vad_map, VAD_SPINT_CLR, + VAD_SPINT_CLR_MASK, VAD_SPINT_CLR_VAD_SPINT); + vad->vstatus = VAD_STATUS_SPINT; + vad_stop(vad); + vad_start(vad); + } + + regmap_read(vad->vad_map, VAD_SLINT,&sl_value); + regmap_read(vad->vad_map, VAD_SLINT_EN,&sl_en); + if (sl_value&sl_en){ + regmap_update_bits(vad->vad_map, VAD_SLINT_CLR, + VAD_SLINT_CLR_MASK, VAD_SLINT_CLR_VAD_SLINT); + vad->vstatus = VAD_STATUS_SLINT; + } +} + +static int vad_trigger(struct vad_params *vad,int cmd) +{ + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if(vad->vswitch) + { + vad_start(vad); + } + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + vad_stop(vad); + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static void vad_init(struct vad_params *vad) +{ + /* left_margin */ + regmap_update_bits(vad->vad_map, VAD_LEFT_MARGIN, + VAD_LEFT_MARGIN_MASK, 0x0); + /* right_margin */ + regmap_update_bits(vad->vad_map, VAD_RIGHT_MARGIN, + VAD_RIGHT_MARGIN_MASK, 0x0); + /*low-energy transition range threshold ——NL*/ + regmap_update_bits(vad->vad_map, VAD_N_LOW_CONT_FRAMES, + VAD_N_LOW_CONT_FRAMES_MASK, 0x3); + /* low-energy transition range */ + regmap_update_bits(vad->vad_map, VAD_N_LOW_SEEK_FRAMES, + VAD_N_LOW_SEEK_FRAMES_MASK, 0x8); + /* high-energy transition range threshold——NH */ + regmap_update_bits(vad->vad_map, VAD_N_HIGH_CONT_FRAMES, + VAD_N_HIGH_CONT_FRAMES_MASK, 0x5); + /* high-energy transition range */ + regmap_update_bits(vad->vad_map, VAD_N_HIGH_SEEK_FRAMES, + VAD_N_HIGH_SEEK_FRAMES_MASK, 0x1E); + /*low-energy voice range threshold——NVL*/ + regmap_update_bits(vad->vad_map, VAD_N_SPEECH_LOW_HIGH_FRAMES, + VAD_N_SPEECH_LOW_HIGH_FRAMES_MASK, 0x2); + /*low-energy voice range*/ + regmap_update_bits(vad->vad_map, VAD_N_SPEECH_LOW_SEEK_FRAMES, + VAD_N_SPEECH_LOW_SEEK_FRAMES_MASK, 0x12); + /*mean silence frame range*/ + regmap_update_bits(vad->vad_map, VAD_MEAN_SIL_FRAMES, + VAD_MEAN_SIL_FRAMES_MASK, 0xA); + /*low-energy threshold scaling factor,12bit(0~0xFFF)*/ + regmap_update_bits(vad->vad_map, VAD_N_ALPHA, + VAD_N_ALPHA_MASK, 0x1A); + /*high-energy threshold scaling factor,12bit(0~0xFFF)*/ + regmap_update_bits(vad->vad_map, VAD_N_BETA, + VAD_N_BETA_MASK, 0x34); + regmap_update_bits(vad->vad_map, VAD_LEFT_WD, + VAD_LEFT_WD_MASK, VAD_LEFT_WD_BIT_15_0); + regmap_update_bits(vad->vad_map, VAD_RIGHT_WD, + VAD_RIGHT_WD_MASK, VAD_RIGHT_WD_BIT_15_0); + regmap_update_bits(vad->vad_map, VAD_LR_SEL, + VAD_LR_SEL_MASK, VAD_LR_SEL_L); + regmap_update_bits(vad->vad_map, VAD_STOP_DELAY, + VAD_STOP_DELAY_MASK, VAD_STOP_DELAY_0_SAMPLE); + regmap_update_bits(vad->vad_map, VAD_ADDR_START, + VAD_ADDR_START_MASK, 0x0); + regmap_update_bits(vad->vad_map, VAD_ADDR_WRAP, + VAD_ADDR_WRAP_MASK, 0x2000); + regmap_update_bits(vad->vad_map, VAD_MEM_SW, + VAD_MEM_SW_MASK, VAD_MEM_SW_TO_AXI); + regmap_update_bits(vad->vad_map, VAD_SPINT_CLR, + VAD_SPINT_CLR_MASK, VAD_SPINT_CLR_VAD_SPINT); + regmap_update_bits(vad->vad_map, VAD_SLINT_CLR, + VAD_SLINT_CLR_MASK, VAD_SLINT_CLR_VAD_SLINT); +} + + +static int vad_switch_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + + return 0; +} + +static int vad_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct i2svad_dev *dev = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = dev->vad.vswitch; + + return 0; +} + +static int vad_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct i2svad_dev *dev = snd_soc_component_get_drvdata(component); + int val; + + val = ucontrol->value.integer.value[0]; + if (val && !dev->vad.vswitch) { + dev->vad.vswitch = true; + } else if (!val && dev->vad.vswitch) { + dev->vad.vswitch = false; + vad_stop(&(dev->vad)); + } + + return 0; +} + + +static int vad_status_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 2; + + return 0; +} + +static int vad_status_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct i2svad_dev *dev = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = dev->vad.vstatus; + dev->vad.vstatus = VAD_STATUS_NORMAL; + + return 0; +} + + +#define SOC_VAD_SWITCH_DECL(xname) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = vad_switch_info, .get = vad_switch_get, \ + .put = vad_switch_put, } + +#define SOC_VAD_STATUS_DECL(xname) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = vad_status_info, .get = vad_status_get, } + + +static const struct snd_kcontrol_new vad_snd_controls[] = { + SOC_VAD_SWITCH_DECL("vad switch"), + SOC_VAD_STATUS_DECL("vad status"), +}; + +static int vad_probe(struct snd_soc_component *component) +{ + struct i2svad_dev *priv = snd_soc_component_get_drvdata(component); + + snd_soc_component_init_regmap(component, priv->vad.vad_map); + snd_soc_add_component_controls(component, vad_snd_controls, + ARRAY_SIZE(vad_snd_controls)); + + return 0; +} + +/* i2s control function*/ +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 i2svad_dev *dev, u32 stream) +{ + u32 i = 0; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + for (i = 0; i < ALL_CHANNEL_NUM; i++) + i2s_write_reg(dev->i2s_base, TER(i), 0); + } else { + for (i = 0; i < ALL_CHANNEL_NUM; i++) + i2s_write_reg(dev->i2s_base, RER(i), 0); + } +} + +static inline void i2s_clear_irqs(struct i2svad_dev *dev, u32 stream) +{ + u32 i = 0; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + for (i = 0; i < ALL_CHANNEL_NUM; i++) + i2s_read_reg(dev->i2s_base, TOR(i)); + } else { + for (i = 0; i < ALL_CHANNEL_NUM; i++) + i2s_read_reg(dev->i2s_base, ROR(i)); + } +} + +static inline void i2s_disable_irqs(struct i2svad_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 i2svad_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 i2svad_dev *dev = dev_id; + bool irq_valid = false; + u32 isr[4]; + int i; + + for (i = 0; i < ALL_CHANNEL_NUM; 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) { + i2svad_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) { + i2svad_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: TX */ + if (isr[i] & ISR_RXFO) { + //dev_err(dev->dev, "RX overrun (ch_id=%d)\n", i); + irq_valid = true; + } + } + + vad_status(&(dev->vad)); + + if (irq_valid) + return IRQ_HANDLED; + else + return IRQ_NONE; +} + +static void i2s_start(struct i2svad_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 i2svad_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 i2svad_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; + + 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); + + return 0; +} + +static void dw_i2s_config(struct i2svad_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 i2svad_clks_get(struct platform_device *pdev, + struct i2svad_dev *dev) +{ + dev->clk_apb_i2svad = devm_clk_get(&pdev->dev, "i2svad_apb"); + if (IS_ERR(dev->clk_apb_i2svad)) { + dev_err(&pdev->dev, "failed to get clk_apb_i2svad: %ld\n", + PTR_ERR(dev->clk_apb_i2svad)); + return PTR_ERR(dev->clk_apb_i2svad); + } + + return 0; +} + +static int i2svad_resets_get(struct platform_device *pdev, + struct i2svad_dev *dev) +{ + struct reset_control_bulk_data resets[] = { + { .id = "apb_i2svad" }, + { .id = "i2svad_srst" }, + }; + int ret = devm_reset_control_bulk_get_exclusive(&pdev->dev, ARRAY_SIZE(resets), resets); + if (ret) + return ret; + + dev->rst_apb_i2svad = resets[0].rstc; + dev->rst_i2svad_srst = resets[1].rstc; + + return 0; +} + +static int i2svad_clk_init(struct platform_device *pdev, + struct i2svad_dev *dev) +{ + int ret; + + ret = clk_prepare_enable(dev->clk_apb_i2svad); + if (ret) { + dev_err(&pdev->dev, "failed to prepare enable clk_audio_root\n"); + return ret; + } + + ret = reset_control_deassert(dev->rst_apb_i2svad); + if (ret) { + printk(KERN_INFO "failed to deassert apb_i2svad\n"); + return ret; + } + + ret = reset_control_deassert(dev->rst_i2svad_srst); + if (ret) { + printk(KERN_INFO "failed to deassert i2svad_srst\n"); + return ret; + } + + return 0; +} + +static int dw_i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct i2svad_dev *dev = snd_soc_dai_get_drvdata(dai); + struct i2s_clk_config_data *config = &dev->config; + 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; + } + } + } + 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 i2svad_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 i2svad_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; + } + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + { + vad_trigger(&(dev->vad),cmd); + } + return ret; +} + +static int dw_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) +{ + struct i2svad_dev *dev = snd_soc_dai_get_drvdata(cpu_dai); + int ret = 0; + + 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_CBS_CFS: + if (dev->capability & DW_I2S_MASTER) + ret = 0; + else + ret = -EINVAL; + break; + case SND_SOC_DAIFMT_CBM_CFS: + case SND_SOC_DAIFMT_CBS_CFM: + ret = -EINVAL; + break; + default: + dev_dbg(dev->dev, "dwc : Invalid master/slave 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 i2svad_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 i2svad_dev *dw_dev = dev_get_drvdata(dev); + + if (dw_dev->capability & DW_I2S_MASTER) + clk_enable(dw_dev->clk); + return 0; +} + +static int dw_i2s_suspend(struct snd_soc_component *component) +{ + struct i2svad_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 i2svad_dev *dev = snd_soc_component_get_drvdata(component); + struct snd_soc_dai *dai; + int stream; + + if (dev->capability & DW_I2S_MASTER) + clk_enable(dev->clk); + + 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 int dw_i2svad_probe(struct snd_soc_component *component) +{ + vad_probe(component); + return 0; +} + +static const struct snd_soc_component_driver dw_i2s_component = { + .name = "dw-i2s", + .probe = dw_i2svad_probe, + .suspend = dw_i2s_suspend, + .resume = dw_i2s_resume, +}; + +/* + * 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 const struct regmap_config sf_i2s_regmap_cfg = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = 0x1000, +}; + +static int dw_configure_dai(struct i2svad_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.formats = SNDRV_PCM_FMTBIT_S16_LE; + 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.formats = SNDRV_PCM_FMTBIT_S16_LE; + 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 i2svad_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 i2svad_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 i2svad_dev *dev; + struct resource *res; + int ret, irq; + 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; + + 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->vad.vad_base = dev->i2s_base; + dev->vad.vad_map = devm_regmap_init_mmio(&pdev->dev, dev->i2s_base, &sf_i2s_regmap_cfg); + if (IS_ERR(dev->vad.vad_map)) { + dev_err(&pdev->dev, "failed to init regmap: %ld\n", + PTR_ERR(dev->vad.vad_map)); + return PTR_ERR(dev->vad.vad_map); + } + + dev->dev = &pdev->dev; + + ret = i2svad_clks_get(pdev, dev); + if (ret) { + dev_err(&pdev->dev, "failed to get i2svad clock\n"); + return ret; + } + + ret = i2svad_resets_get(pdev, dev); + if (ret) { + dev_err(&pdev->dev, "failed to get i2svad reset controls\n"); + return ret; + } + + ret = i2svad_clk_init(pdev, dev); + if (ret) { + dev_err(&pdev->dev, "failed to initialize i2svad clock\n"); + return ret; + } + + irq = platform_get_irq(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; + } + } + + 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 = i2svad_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; + } + } + + vad_init(&(dev->vad)); + 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 i2svad_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 = "starfive,sf-i2svad", }, + {}, +}; + +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 = "sf-i2svad", + .of_match_table = of_match_ptr(dw_i2s_of_match), + .pm = &dwc_pm_ops, + }, +}; + +module_platform_driver(dw_i2s_driver); + +MODULE_AUTHOR("jenny zhang "); +MODULE_DESCRIPTION("starfive I2SVAD SoC Interface"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:sf-i2svad"); diff --git a/sound/soc/starfive/starfive_i2svad.h b/sound/soc/starfive/starfive_i2svad.h new file mode 100644 index 0000000000000..7f1b81bc9e926 --- /dev/null +++ b/sound/soc/starfive/starfive_i2svad.h @@ -0,0 +1,245 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 StarFive Technology Co., Ltd. + */ +#ifndef __SF_I2SVAD_H +#define __SF_I2SVAD_H + +#include +#include +#include +#include +#include +#include + +/* 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 + +/* VAD Registers */ +#define VAD_LEFT_MARGIN 0x800 /* left_margin */ +#define VAD_RIGHT_MARGIN 0x804 /* right_margin */ +#define VAD_N_LOW_CONT_FRAMES 0x808 /* low-energy transition range threshold ——NL*/ +#define VAD_N_LOW_SEEK_FRAMES 0x80C /* low-energy transition range */ +#define VAD_N_HIGH_CONT_FRAMES 0x810 /* high-energy transition range threshold——NH */ +#define VAD_N_HIGH_SEEK_FRAMES 0x814 /* high-energy transition range */ +#define VAD_N_SPEECH_LOW_HIGH_FRAMES 0x818 /* low-energy voice range threshold——NVL*/ +#define VAD_N_SPEECH_LOW_SEEK_FRAMES 0x81C /* low-energy voice range*/ +#define VAD_MEAN_SIL_FRAMES 0x820 /* mean silence frame range*/ +#define VAD_N_ALPHA 0x824 /* low-energy threshold scaling factor,12bit(0~0xFFF)*/ +#define VAD_N_BETA 0x828 /* high-energy threshold scaling factor,12bit(0~0xFFF)*/ +#define VAD_FIFO_DEPTH 0x82C /* status register for VAD */ +#define VAD_LR_SEL 0x840 /* L/R channel data selection for processing */ +#define VAD_SW 0x844 /* push enable signal*/ +#define VAD_LEFT_WD 0x848 /* select left channel*/ +#define VAD_RIGHT_WD 0x84C /* select right channel*/ +#define VAD_STOP_DELAY 0x850 /* delay stop for 0-3 samples*/ +#define VAD_ADDR_START 0x854 /* vad memory start address, align with 64bit*/ +#define VAD_ADDR_WRAP 0x858 /* vad memory highest address for Push, align with 64bit,(addr_wrap-1) is the max physical address*/ +#define VAD_MEM_SW 0x85C /* xmem switch */ +#define VAD_SPINT_CLR 0x860 /* clear vad_spint interrup status*/ +#define VAD_SPINT_EN 0x864 /* disable/enable vad_spint from vad_flag rising edge*/ +#define VAD_SLINT_CLR 0x868 /* clear vad_slint interrup status*/ +#define VAD_SLINT_EN 0x86C /* disable/enable vad_slint from vad_flag falling edge*/ +#define VAD_RAW_SPINT 0x870 /* status of spint before vad_spint_en*/ +#define VAD_RAW_SLINT 0x874 /* status of slint before vad_slint_en*/ +#define VAD_SPINT 0x878 /* status of spint after vad_spint_en*/ +#define VAD_SLINT 0x87C /* status of slint before vad_slint_en*/ +#define VAD_XMEM_ADDR 0x880 /* next xmem address ,align to 16bi*/ +#define VAD_I2S_CTRL_REG_ADDR 0x884 + +/* + * vad parameter register fields + */ +#define VAD_LEFT_MARGIN_MASK GENMASK(4, 0) +#define VAD_RIGHT_MARGIN_MASK GENMASK(4, 0) +#define VAD_N_LOW_CONT_FRAMES_MASK GENMASK(4, 0) +#define VAD_N_LOW_SEEK_FRAMES_MASK GENMASK(4, 0) +#define VAD_N_HIGH_CONT_FRAMES_MASK GENMASK(4, 0) +#define VAD_N_HIGH_SEEK_FRAMES_MASK GENMASK(4, 0) +#define VAD_N_SPEECH_LOW_HIGH_FRAMES_MASK GENMASK(4, 0) +#define VAD_N_SPEECH_LOW_SEEK_FRAMES_MASK GENMASK(4, 0) +#define VAD_MEAN_SIL_FRAMES_MASK GENMASK(4, 0) +#define VAD_N_ALPHA_MASK GENMASK(11, 0) +#define VAD_N_BETA_MASK GENMASK(11, 0) +#define VAD_LR_SEL_MASK GENMASK(0, 0) +#define VAD_LR_SEL_L (0 << 0) +#define VAD_LR_SEL_R (1 << 0) + +#define VAD_SW_MASK GENMASK(1, 0) +#define VAD_SW_VAD_XMEM_ENABLE (1 << 0) +#define VAD_SW_VAD_XMEM_DISABLE (0 << 0) +#define VAD_SW_ADC_ENABLE (1 << 1) +#define VAD_SW_ADC_DISABLE (0 << 1) + + +#define VAD_LEFT_WD_MASK GENMASK(0, 0) +#define VAD_LEFT_WD_BIT_31_16 (1 << 1) +#define VAD_LEFT_WD_BIT_15_0 (0 << 1) + + +#define VAD_RIGHT_WD_MASK GENMASK(0, 0) +#define VAD_RIGHT_WD_BIT_31_16 (1 << 1) +#define VAD_RIGHT_WD_BIT_15_0 (0 << 1) + + +#define VAD_STOP_DELAY_MASK GENMASK(1, 0) +#define VAD_STOP_DELAY_0_SAMPLE 0 +#define VAD_STOP_DELAY_1_SAMPLE 1 +#define VAD_STOP_DELAY_2_SAMPLE 2 +#define VAD_STOP_DELAY_3_SAMPLE 3 + +#define VAD_ADDR_START_MASK GENMASK(12, 0) +#define VAD_ADDR_WRAP_MASK GENMASK(13, 0) +#define VAD_MEM_SW_MASK GENMASK(0, 0) +#define VAD_SPINT_CLR_MASK GENMASK(0, 0) +#define VAD_SPINT_EN_MASK GENMASK(0, 0) +#define VAD_SLINT_CLR_MASK GENMASK(0, 0) +#define VAD_SLINT_EN_MASK GENMASK(0, 0) +#define VAD_I2S_CTRL_REG_ADDR_MASK GENMASK(0, 0) + +#define VAD_MEM_SW_TO_VAD (1 << 0) +#define VAD_MEM_SW_TO_AXI (0 << 0) + +#define VAD_SPINT_CLR_VAD_SPINT (1 << 0) + +#define VAD_SPINT_EN_ENABLE (1 << 0) +#define VAD_SPINT_EN_DISABLE (0 << 0) + +#define VAD_SLINT_CLR_VAD_SLINT (1 << 0) + +#define VAD_SLINT_EN_ENABLE (1 << 0) +#define VAD_SLINT_EN_DISABLE (0 << 0) + +#define VAD_STATUS_NORMAL 0 +#define VAD_STATUS_SPINT 1 +#define VAD_STATUS_SLINT 2 + +/* + * 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 +#define ALL_CHANNEL_NUM 4 + + +union dw_i2s_snd_dma_data { + struct i2s_dma_data pd; + struct snd_dmaengine_dai_dma_data dt; +}; + +struct vad_params { + void __iomem *vad_base; + struct regmap *vad_map; + unsigned int vswitch; + unsigned int vstatus; /*vad detect status: 1:SPINT 2:SLINT 0:normal*/ +}; + +struct i2svad_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; + struct device *dev; + u32 ccr; + u32 xfer_resolution; + u32 fifo_th; + + struct clk *clk_apb_i2svad; + struct reset_control *rst_apb_i2svad; + struct reset_control *rst_i2svad_srst; + + /* 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 i2svad_dev *dev, + struct snd_pcm_runtime *runtime, unsigned int tx_ptr, + bool *period_elapsed); + unsigned int (*rx_fn)(struct i2svad_dev *dev, + struct snd_pcm_runtime *runtime, unsigned int rx_ptr, + bool *period_elapsed); + unsigned int tx_ptr; + unsigned int rx_ptr; + + struct vad_params vad; +}; + +#if IS_ENABLED(CONFIG_SND_STARFIVE_I2SVAD_PCM) +void i2svad_pcm_push_tx(struct i2svad_dev *dev); +void i2svad_pcm_pop_rx(struct i2svad_dev *dev); +int i2svad_pcm_register(struct platform_device *pdev); +#else +void i2svad_pcm_push_tx(struct i2svad_dev *dev) { } +void i2svad_pcm_pop_rx(struct i2svad_dev *dev) { } +int i2svad_pcm_register(struct platform_device *pdev) +{ + return -EINVAL; +} +#endif + +#endif diff --git a/sound/soc/starfive/starfive_i2svad_pcm.c b/sound/soc/starfive/starfive_i2svad_pcm.c new file mode 100644 index 0000000000000..88e6258a53f8b --- /dev/null +++ b/sound/soc/starfive/starfive_i2svad_pcm.c @@ -0,0 +1,248 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 StarFive Technology Co., Ltd. + */ +#include +#include +#include +#include +#include "starfive_i2svad.h" + +#define BUFFER_BYTES_MAX (3 * 2 * 8 * PERIOD_BYTES_MIN) +#define PERIOD_BYTES_MIN 4096 +#define PERIODS_MIN 2 + +#define i2svad_pcm_tx_fn(sample_bits) \ +static unsigned int i2svad_pcm_tx_##sample_bits(struct i2svad_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 i2svad_pcm_rx_fn(sample_bits) \ +static unsigned int i2svad_pcm_rx_##sample_bits(struct i2svad_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; \ +} + +i2svad_pcm_tx_fn(16); +i2svad_pcm_rx_fn(16); + +#undef i2svad_pcm_tx_fn +#undef i2svad_pcm_rx_fn + +static const struct snd_pcm_hardware i2svad_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, + .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 i2svad_pcm_transfer(struct i2svad_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 i2svad_pcm_push_tx(struct i2svad_dev *dev) +{ + i2svad_pcm_transfer(dev, true); +} + +void i2svad_pcm_pop_rx(struct i2svad_dev *dev) +{ + i2svad_pcm_transfer(dev, false); +} + +static int i2svad_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 i2svad_dev *dev = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); + + snd_soc_set_runtime_hwparams(substream, &i2svad_pcm_hardware); + snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); + runtime->private_data = dev; + + return 0; +} + +static int i2svad_pcm_close(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + synchronize_rcu(); + return 0; +} + +static int i2svad_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 i2svad_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 = i2svad_pcm_tx_16; + dev->rx_fn = i2svad_pcm_rx_16; + break; + default: + dev_err(dev->dev, "invalid format\n"); + return -EINVAL; + } + + return 0; +} + +static int i2svad_pcm_trigger(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int cmd) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct i2svad_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 i2svad_pcm_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct i2svad_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 i2svad_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) +{ + size_t size = i2svad_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 i2svad_pcm_component = { + .open = i2svad_pcm_open, + .close = i2svad_pcm_close, + .hw_params = i2svad_pcm_hw_params, + .trigger = i2svad_pcm_trigger, + .pointer = i2svad_pcm_pointer, + .pcm_construct = i2svad_pcm_new, +}; + +int i2svad_pcm_register(struct platform_device *pdev) +{ + return devm_snd_soc_register_component(&pdev->dev, &i2svad_pcm_component, + NULL, 0); +} diff --git a/sound/soc/starfive/starfive_pdm.c b/sound/soc/starfive/starfive_pdm.c new file mode 100644 index 0000000000000..90850a4aa306a --- /dev/null +++ b/sound/soc/starfive/starfive_pdm.c @@ -0,0 +1,361 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 StarFive Technology Co., Ltd. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "starfive_pdm.h" + +#define AUDIOC_CLK (12288000) +#define PDM_MUL (128) + +struct sf_pdm { + struct regmap *pdm_map; + struct regmap *clk_map; + struct clk *clk; +}; + +static const DECLARE_TLV_DB_SCALE(volume_tlv, -9450, 150, 0); + +static const struct snd_kcontrol_new sf_pdm_snd_controls[] = { + SOC_SINGLE("DC compensation Control", PDM_DMIC_CTRL0, 30, 1, 0), + SOC_SINGLE("High Pass Filter Control", PDM_DMIC_CTRL0, 28, 1, 0), + SOC_SINGLE("Left Channel Volume Control", PDM_DMIC_CTRL0, 23, 1, 0), + SOC_SINGLE("Right Channel Volume Control", PDM_DMIC_CTRL0, 22, 1, 0), + SOC_SINGLE_TLV("Volume", PDM_DMIC_CTRL0, 16, 0x3F, 1, volume_tlv), + SOC_SINGLE("Data MSB Shift", PDM_DMIC_CTRL0, 1, 7, 0), + SOC_SINGLE("SCALE", PDM_DC_SCALE0, 0, 0x3F, 0), + SOC_SINGLE("DC offset", PDM_DC_SCALE0, 8, 0xFFFFF, 0), +}; + +static int sf_pdm_set_mclk(struct regmap *map, unsigned int clk, unsigned int weight) +{ + int mclk_div,bclk_div,lrclk_div; + u32 pdm_div; + + /* + audio source clk:12288000, mclk_div:4, mclk:3M + support 8K/16K/32K/48K sample reate + suapport 16/24/32 bit weight + bit weight 32 + mclk bclk lrclk + 3M 1.5M 48K + 3M 1M 32K + 3M 0.5M 16K + 3M 0.25M 8K + + bit weight 24,set lrclk_div as 32 + mclk bclk lrclk + 3M 1.5M 48K + 3M 1M 32K + 3M 0.5M 16K + 3M 0.25M 8K + + bit weight 16 + mclk bclk lrclk + 3M 0.75M 48K + 3M 0.5M 32K + 3M 0.25M 16K + 3M 0.125M 8K + */ + + switch (clk) { + case 8000: + case 16000: + case 32000: + case 48000: + break; + default: + printk(KERN_ERR "sample rate:%d\n", clk); + return -EINVAL; + } + + switch (weight) { + case 16: + case 24: + case 32: + break; + default: + printk(KERN_ERR "bit weight:%d\n", weight); + return -EINVAL; + } + + if (24 == weight) { + weight = 32; + } + + mclk_div = 4; + bclk_div = AUDIOC_CLK/mclk_div/(clk*weight); + lrclk_div = weight; + + /* PDM MCLK = 128*LRCLK */ + pdm_div = AUDIOC_CLK/(PDM_MUL*clk); + + regmap_update_bits(map, AUDIO_CLK_ADC_MCLK, 0x0F, mclk_div); + regmap_update_bits(map, AUDIO_CLK_I2SADC_BCLK, 0x1F, bclk_div); + regmap_update_bits(map, AUDIO_CLK_ADC_LRCLK, 0x3F, lrclk_div); + regmap_update_bits(map, AUDIO_CLK_PDM_CLK, 0x0F, pdm_div); + + return 0; +} + +static void sf_pdm_enable(struct regmap *map) +{ + /* Enable PDM */ + regmap_update_bits(map, PDM_DMIC_CTRL0, 0x01<pdm_map); + return 0; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + sf_pdm_disable(priv->pdm_map); + return 0; + + default: + return -EINVAL; + } +} + +static int sf_pdm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct sf_pdm *priv = snd_soc_dai_get_drvdata(dai); + unsigned int rate = params_rate(params); + unsigned int width; + int ret; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + return 0; + + width = params_width(params); + switch (width) { + case 16: + case 24: + case 32: + break; + default: + dev_err(dai->dev, "unsupported sample width\n"); + return -EINVAL; + } + + ret = sf_pdm_set_mclk(priv->clk_map, rate, width); + if (ret < 0) { + dev_err(dai->dev, "unsupported sample rate\n"); + return -EINVAL; + } + + return 0; +} + +static const struct snd_soc_dai_ops sf_pdm_dai_ops = { + .trigger = sf_pdm_trigger, + .hw_params = sf_pdm_hw_params, +}; + +static int sf_pdm_dai_probe(struct snd_soc_dai *dai) +{ + struct sf_pdm *priv = snd_soc_dai_get_drvdata(dai); + + /* Reset */ + regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0, + 0x01<pdm_map, PDM_DMIC_CTRL0, + 0x01<pdm_map); + + /* MUTE */ + regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0, + 0x3F<pdm_map, PDM_DMIC_CTRL0, + 0x3F<pdm_map, PDM_DMIC_CTRL0, + 0x01<pdm_map, PDM_DMIC_CTRL0, + 0x01<pdm_map, PDM_DMIC_CTRL0, + 0x01<pdm_map, PDM_DMIC_CTRL0, + 0x01<pdm_map, PDM_DMIC_CTRL0, + 0x07<pdm_map, PDM_DC_SCALE0, 0x3F, 0x08); + + /* DC offset:0 */ + regmap_update_bits(priv->pdm_map, PDM_DC_SCALE0, + 0xFFFFF<pdm_map, PDM_DMIC_CTRL0, + 0x3F<pdm_map); + snd_soc_add_component_controls(component, sf_pdm_snd_controls, + ARRAY_SIZE(sf_pdm_snd_controls)); + + return 0; +} + +static const struct snd_soc_component_driver sf_pdm_component_drv = { + .name = "sf-pdm", + .probe = pdm_probe, +}; + +static const struct regmap_config sf_pdm_regmap_cfg = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = 0x20, +}; + +static const struct regmap_config sf_audio_clk_regmap_cfg = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = 0x100, +}; + +static int sf_pdm_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct sf_pdm *priv; + struct resource *res; + void __iomem *regs; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + platform_set_drvdata(pdev, priv); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pdm"); + regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + priv->pdm_map = devm_regmap_init_mmio(dev, regs, &sf_pdm_regmap_cfg); + if (IS_ERR(priv->pdm_map)) { + dev_err(dev, "failed to init regmap: %ld\n", + PTR_ERR(priv->pdm_map)); + return PTR_ERR(priv->pdm_map); + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "audio-clk"); + regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + priv->clk_map = devm_regmap_init_mmio(dev, regs, &sf_audio_clk_regmap_cfg); + if (IS_ERR(priv->clk_map)) { + dev_err(dev, "failed to init regmap: %ld\n", + PTR_ERR(priv->clk_map)); + return PTR_ERR(priv->clk_map); + } + + return devm_snd_soc_register_component(dev, &sf_pdm_component_drv, + &sf_pdm_dai_drv, 1); +} + +static int sf_pdm_dev_remove(struct platform_device *pdev) +{ + return 0; +} +static const struct of_device_id sf_pdm_of_match[] = { + {.compatible = "starfive,sf-pdm",}, + {} +}; +MODULE_DEVICE_TABLE(of, sf_pdm_of_match); + +static struct platform_driver sf_pdm_driver = { + + .driver = { + .name = "sf-pdm", + .of_match_table = sf_pdm_of_match, + }, + .probe = sf_pdm_probe, + .remove = sf_pdm_dev_remove, +}; +module_platform_driver(sf_pdm_driver); + +MODULE_AUTHOR("michael.yan "); +MODULE_DESCRIPTION("starfive PDM Controller Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/starfive/starfive_pdm.h b/sound/soc/starfive/starfive_pdm.h new file mode 100644 index 0000000000000..341ea7616aa47 --- /dev/null +++ b/sound/soc/starfive/starfive_pdm.h @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 StarFive Technology Co., Ltd. + */ +#ifndef __SND_SOC_STARFIVE_SPDIF_H +#define __SND_SOC_STARFIVE_PDM_H + +#include +#include +#include +#include +#include +#include +#include + +#define PDM_DMIC_CTRL0 (0x00) +#define PDM_DC_SCALE0 (0x04) +#define PDM_DMIC_CTRL1 (0x10) +#define PDM_DC_SCALE1 (0x14) + +/* PDM CTRL OFFSET */ +#define PDM_DMIC_MSB_SHIFT_OFFSET (1) +#define PDM_DMIC_VOL_OFFSET (16) +#define PDM_DMIC_RVOL_OFFSET (22) +#define PDM_DMIC_LVOL_OFFSET (23) +#define PDM_DMIC_I2SMODE_OFFSET (24) +#define PDM_DMIC_ENHPF_OFFSET (28) +#define PDM_DMIC_FASTMODE_OFFSET (29) +#define PDM_DMIC_DCBPS_OFFSET (30) +#define PDM_DMIC_SW_RSTN_OFFSET (31) + +/* PDM SCALE OFFSET */ +#define PDM_DMIC_DCOFF3_OFFSET (24) +#define PDM_DMIC_DCOFF2_OFFSET (16) +#define PDM_DMIC_DCOFF1_OFFSET (8) +#define PDM_DMIC_SCALE_OFFSET (0) + +#define AUDIO_CLK_ADC_MCLK 0x0 +#define AUDIO_CLK_I2SADC_BCLK 0xC +#define AUDIO_CLK_ADC_LRCLK 0x14 +#define AUDIO_CLK_PDM_CLK 0x1C + +#endif /* __SND_SOC_STARFIVE_PDM_H */ diff --git a/sound/soc/starfive/starfive_pwmdac.c b/sound/soc/starfive/starfive_pwmdac.c new file mode 100644 index 0000000000000..70061cc6ab228 --- /dev/null +++ b/sound/soc/starfive/starfive_pwmdac.c @@ -0,0 +1,901 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PWMDAC driver for the StarFive JH7100 SoC + * + * Copyright (C) 2021 StarFive Technology Co., Ltd. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pwmdac.h" +#include + +struct ct_pwmdac { + char *name; + unsigned int vals; +}; + +static const struct ct_pwmdac pwmdac_ct_shift_bit[] = { + { .name = "8bit", .vals = PWMDAC_SHIFT_8 }, + { .name = "10bit", .vals = PWMDAC_SHIFT_10 } +}; + +static const struct ct_pwmdac pwmdac_ct_duty_cycle[] = { + { .name = "left", .vals = PWMDAC_CYCLE_LEFT }, + { .name = "right", .vals = PWMDAC_CYCLE_RIGHT }, + { .name = "center", .vals = PWMDAC_CYCLE_CENTER } +}; + +static const struct ct_pwmdac pwmdac_ct_data_mode[] = { + { .name = "unsinged", .vals = UNSINGED_DATA }, + { .name = "inverter", .vals = INVERTER_DATA_MSB } +}; + +static const struct ct_pwmdac pwmdac_ct_lr_change[] = { + { .name = "no_change", .vals = NO_CHANGE }, + { .name = "change", .vals = CHANGE } +}; + +static const struct ct_pwmdac pwmdac_ct_shift[] = { + { .name = "left 0 bit", .vals = PWMDAC_DATA_LEFT_SHIFT_BIT_0 }, + { .name = "left 1 bit", .vals = PWMDAC_DATA_LEFT_SHIFT_BIT_1 }, + { .name = "left 2 bit", .vals = PWMDAC_DATA_LEFT_SHIFT_BIT_2 }, + { .name = "left 3 bit", .vals = PWMDAC_DATA_LEFT_SHIFT_BIT_3 }, + { .name = "left 4 bit", .vals = PWMDAC_DATA_LEFT_SHIFT_BIT_4 }, + { .name = "left 5 bit", .vals = PWMDAC_DATA_LEFT_SHIFT_BIT_5 }, + { .name = "left 6 bit", .vals = PWMDAC_DATA_LEFT_SHIFT_BIT_6 } +}; + +static int pwmdac_shift_bit_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + unsigned int items = ARRAY_SIZE(pwmdac_ct_shift_bit); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = items; + if (uinfo->value.enumerated.item >= items) { + uinfo->value.enumerated.item = items - 1; + } + strcpy(uinfo->value.enumerated.name, + pwmdac_ct_shift_bit[uinfo->value.enumerated.item].name); + + return 0; +} +static int pwmdac_shift_bit_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct sf_pwmdac_dev *dev = snd_soc_component_get_drvdata(component); + unsigned int item; + + if (dev->shift_bit == pwmdac_ct_shift_bit[0].vals) + item = 0; + else + item = 1; + + ucontrol->value.enumerated.item[0] = item; + + return 0; +} + +static int pwmdac_shift_bit_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct sf_pwmdac_dev *dev = snd_soc_component_get_drvdata(component); + int sel = ucontrol->value.enumerated.item[0]; + unsigned int items = ARRAY_SIZE(pwmdac_ct_shift_bit); + + if (sel > items) + return 0; + + switch (sel) { + case 1: + dev->shift_bit = pwmdac_ct_shift_bit[1].vals; + break; + default: + dev->shift_bit = pwmdac_ct_shift_bit[0].vals; + break; + } + + return 0; +} + +static int pwmdac_duty_cycle_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + unsigned int items = ARRAY_SIZE(pwmdac_ct_duty_cycle); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = items; + if (uinfo->value.enumerated.item >= items) + uinfo->value.enumerated.item = items - 1; + strcpy(uinfo->value.enumerated.name, + pwmdac_ct_duty_cycle[uinfo->value.enumerated.item].name); + + return 0; +} + +static int pwmdac_duty_cycle_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct sf_pwmdac_dev *dev = snd_soc_component_get_drvdata(component); + + ucontrol->value.enumerated.item[0] = dev->duty_cycle; + return 0; +} + +static int pwmdac_duty_cycle_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct sf_pwmdac_dev *dev = snd_soc_component_get_drvdata(component); + int sel = ucontrol->value.enumerated.item[0]; + unsigned int items = ARRAY_SIZE(pwmdac_ct_duty_cycle); + + if (sel > items) + return 0; + + dev->duty_cycle = pwmdac_ct_duty_cycle[sel].vals; + return 0; +} + +/* +static int pwmdac_datan_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 1; + uinfo->value.integer.max = PWMDAC_SAMPLE_CNT_511; + uinfo->value.integer.step = 1; + return 0; +} + +static int pwmdac_datan_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct sf_pwmdac_dev *dev = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = dev->datan; + + return 0; +} + +static int pwmdac_datan_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct sf_pwmdac_dev *dev = snd_soc_component_get_drvdata(component); + int sel = ucontrol->value.integer.value[0]; + + if (sel > PWMDAC_SAMPLE_CNT_511) + return 0; + + dev->datan = sel; + + return 0; +} +*/ + +static int pwmdac_data_mode_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + unsigned int items = ARRAY_SIZE(pwmdac_ct_data_mode); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = items; + if (uinfo->value.enumerated.item >= items) + uinfo->value.enumerated.item = items - 1; + strcpy(uinfo->value.enumerated.name, + pwmdac_ct_data_mode[uinfo->value.enumerated.item].name); + + return 0; +} + +static int pwmdac_data_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct sf_pwmdac_dev *dev = snd_soc_component_get_drvdata(component); + + ucontrol->value.enumerated.item[0] = dev->data_mode; + return 0; +} + +static int pwmdac_data_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct sf_pwmdac_dev *dev = snd_soc_component_get_drvdata(component); + int sel = ucontrol->value.enumerated.item[0]; + unsigned int items = ARRAY_SIZE(pwmdac_ct_data_mode); + + if (sel > items) + return 0; + + dev->data_mode = pwmdac_ct_data_mode[sel].vals; + return 0; +} + +static int pwmdac_shift_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + unsigned int items = ARRAY_SIZE(pwmdac_ct_shift); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = items; + if (uinfo->value.enumerated.item >= items) + uinfo->value.enumerated.item = items - 1; + strcpy(uinfo->value.enumerated.name, + pwmdac_ct_shift[uinfo->value.enumerated.item].name); + + return 0; +} + +static int pwmdac_shift_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct sf_pwmdac_dev *dev = snd_soc_component_get_drvdata(component); + unsigned int item = dev->shift; + + ucontrol->value.enumerated.item[0] = pwmdac_ct_shift[item].vals; + return 0; +} + +static int pwmdac_shift_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct sf_pwmdac_dev *dev = snd_soc_component_get_drvdata(component); + int sel = ucontrol->value.enumerated.item[0]; + unsigned int items = ARRAY_SIZE(pwmdac_ct_shift); + + if (sel > items) + return 0; + + dev->shift = pwmdac_ct_shift[sel].vals; + return 0; +} + +static int pwmdac_lr_change_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + unsigned int items = ARRAY_SIZE(pwmdac_ct_lr_change); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = items; + if (uinfo->value.enumerated.item >= items) + uinfo->value.enumerated.item = items - 1; + strcpy(uinfo->value.enumerated.name, + pwmdac_ct_lr_change[uinfo->value.enumerated.item].name); + + return 0; +} + +static int pwmdac_lr_change_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct sf_pwmdac_dev *dev = snd_soc_component_get_drvdata(component); + + ucontrol->value.enumerated.item[0] = dev->lr_change; + return 0; +} + +static int pwmdac_lr_change_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct sf_pwmdac_dev *dev = snd_soc_component_get_drvdata(component); + int sel = ucontrol->value.enumerated.item[0]; + unsigned int items = ARRAY_SIZE(pwmdac_ct_lr_change); + + if (sel > items) + return 0; + + dev->lr_change = pwmdac_ct_lr_change[sel].vals; + return 0; +} + +static inline void pwmdc_write_reg(void __iomem *io_base, int reg, u32 val) +{ + writel(val, io_base + reg); +} + +static inline u32 pwmdc_read_reg(void __iomem *io_base, int reg) +{ + return readl(io_base + reg); +} + +/* + * 32bit-4byte +*/ +static void pwmdac_set_ctrl_enable(struct sf_pwmdac_dev *dev) +{ + u32 date; + date = pwmdc_read_reg(dev->pwmdac_base, PWMDAC_CTRL); + pwmdc_write_reg(dev->pwmdac_base, PWMDAC_CTRL, date | BIT(0) ); +} + +/* + * 32bit-4byte +*/ +static void pwmdac_set_ctrl_disable(struct sf_pwmdac_dev *dev) +{ + u32 date; + date = pwmdc_read_reg(dev->pwmdac_base, PWMDAC_CTRL); + pwmdc_write_reg(dev->pwmdac_base, PWMDAC_CTRL, date & ~ BIT(0)); +} + +/* + * 8:8-bit + * 10:10-bit +*/ +static void pwmdac_set_ctrl_shift(struct sf_pwmdac_dev *dev, u8 data) +{ + u32 value = 0; + + if (data == 8) { + value = (~((~value) | 0x02)); + pwmdc_write_reg(dev->pwmdac_base , PWMDAC_CTRL, value); + } + else if(data == 10){ + value |= 0x02; + pwmdc_write_reg(dev->pwmdac_base , PWMDAC_CTRL, value); + } +} + +/* + * 00:left + * 01:right + * 10:center +*/ +static void pwmdac_set_ctrl_dutyCycle(struct sf_pwmdac_dev *dev, u8 data) +{ + u32 value = 0; + + value = pwmdc_read_reg(dev->pwmdac_base , PWMDAC_CTRL); + if (data == 0) { //left + value = (~((~value) | (0x03<<2))); + pwmdc_write_reg(dev->pwmdac_base , PWMDAC_CTRL, value); + } + else if (data == 1) { //right + value = (~((~value) | (0x01<<3))) | (0x01<<2); + pwmdc_write_reg(dev->pwmdac_base , PWMDAC_CTRL, value); + } + else if (data == 2) { //center + value = (~((~value) | (0x01<<2))) | (0x01<<3); + pwmdc_write_reg(dev->pwmdac_base , PWMDAC_CTRL, value); + } +} + + +static void pwmdac_set_ctrl_N(struct sf_pwmdac_dev *dev, u16 data) +{ + u32 value = 0; + + value = pwmdc_read_reg(dev->pwmdac_base , PWMDAC_CTRL); + pwmdc_write_reg(dev->pwmdac_base , PWMDAC_CTRL, (value & 0xF) | ((data - 1) << 4)); +} + + +static void pwmdac_LR_data_change(struct sf_pwmdac_dev *dev, u8 data) +{ + u32 value = 0; + + value = pwmdc_read_reg(dev->pwmdac_base , PWMDAC_CTRL); + switch (data) { + case NO_CHANGE: + value &= (~SFC_PWMDAC_LEFT_RIGHT_DATA_CHANGE); + break; + case CHANGE: + value |= SFC_PWMDAC_LEFT_RIGHT_DATA_CHANGE; + break; + } + pwmdc_write_reg(dev->pwmdac_base, PWMDAC_CTRL, value); +} + +static void pwmdac_data_mode(struct sf_pwmdac_dev *dev, u8 data) +{ + u32 value = 0; + + value = pwmdc_read_reg(dev->pwmdac_base , PWMDAC_CTRL); + if (data == UNSINGED_DATA) { + value &= (~SFC_PWMDAC_DATA_MODE); + } + else if (data == INVERTER_DATA_MSB) { + value |= SFC_PWMDAC_DATA_MODE; + } + pwmdc_write_reg(dev->pwmdac_base,PWMDAC_CTRL, value); +} + +static int pwmdac_data_shift(struct sf_pwmdac_dev *dev,u8 data) +{ + u32 value = 0; + + if ((data < PWMDAC_DATA_LEFT_SHIFT_BIT_0) || (data > PWMDAC_DATA_LEFT_SHIFT_BIT_7)) { + return -1; + } + + value = pwmdc_read_reg(dev->pwmdac_base , PWMDAC_CTRL); + value &= ( ~ ( PWMDAC_DATA_LEFT_SHIFT_BIT_ALL << 15 ) ); + value |= (data<<15); + pwmdc_write_reg(dev->pwmdac_base , PWMDAC_CTRL, value); + return 0; +} + +static int get_pwmdac_fifo_state(struct sf_pwmdac_dev *dev) +{ + u32 value; + + value = pwmdc_read_reg(dev->pwmdac_base , PWMDAC_SATAE); + if ((value & 0x02) == 0) + return FIFO_UN_FULL; + + return FIFO_FULL; +} + + +static void pwmdac_set(struct sf_pwmdac_dev *dev) +{ + ///8-bit + left + N=16 + pwmdac_set_ctrl_shift(dev, dev->shift_bit); + pwmdac_set_ctrl_dutyCycle(dev, dev->duty_cycle); + pwmdac_set_ctrl_N(dev, dev->datan); + pwmdac_set_ctrl_enable(dev); + + pwmdac_LR_data_change(dev, dev->lr_change); + pwmdac_data_mode(dev, dev->data_mode); + if (dev->shift) { + pwmdac_data_shift(dev, dev->shift); + } +} + +static void pwmdac_stop(struct sf_pwmdac_dev *dev) +{ + pwmdac_set_ctrl_disable(dev); +} + +static int pwmdac_config(struct sf_pwmdac_dev *dev) +{ + switch (dev->mode) { + case shift_8Bit_unsigned: + case shift_8Bit_unsigned_dataShift: + /* 8 bit, unsigned */ + dev->shift_bit = PWMDAC_SHIFT_8; + dev->duty_cycle = PWMDAC_CYCLE_CENTER; + dev->datan = PWMDAC_SAMPLE_CNT_8; + dev->data_mode = UNSINGED_DATA; + break; + + case shift_8Bit_inverter: + case shift_8Bit_inverter_dataShift: + /* 8 bit, invert */ + dev->shift_bit = PWMDAC_SHIFT_8; + dev->duty_cycle = PWMDAC_CYCLE_CENTER; + dev->datan = PWMDAC_SAMPLE_CNT_8; + dev->data_mode = INVERTER_DATA_MSB; + break; + + case shift_10Bit_unsigned: + case shift_10Bit_unsigned_dataShift: + /* 10 bit, unsigend */ + dev->shift_bit = PWMDAC_SHIFT_10; + dev->duty_cycle = PWMDAC_CYCLE_CENTER; + dev->datan = PWMDAC_SAMPLE_CNT_8; + dev->data_mode = UNSINGED_DATA; + break; + + case shift_10Bit_inverter: + case shift_10Bit_inverter_dataShift: + /* 10 bit, invert */ + dev->shift_bit = PWMDAC_SHIFT_10; + dev->duty_cycle = PWMDAC_CYCLE_CENTER; + dev->datan = PWMDAC_SAMPLE_CNT_8; + dev->data_mode = INVERTER_DATA_MSB; + break; + + default: + return -1; + } + + if ((dev->mode == shift_8Bit_unsigned_dataShift) || (dev->mode == shift_8Bit_inverter_dataShift) + || (dev->mode == shift_10Bit_unsigned_dataShift) || (dev->mode == shift_10Bit_inverter_dataShift)) { + dev->shift = 4; /*0~7*/ + } else { + dev->shift = 0; + } + dev->lr_change = NO_CHANGE; + return 0; +} + +static int sf_pwmdac_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + //struct sf_pwmdac_dev *dev = snd_soc_dai_get_drvdata(dai); + //pwmdac_set(dev); + return 0; +} + +static int pwmdac_tx_thread(void *dev) +{ + struct sf_pwmdac_dev *pwmdac_dev = (struct sf_pwmdac_dev *)dev; + + set_current_state(TASK_INTERRUPTIBLE); + while (!schedule_timeout(usecs_to_jiffies(50))) { + if (pwmdac_dev->tx_thread_exit) + break; + if (get_pwmdac_fifo_state(pwmdac_dev)==0) { + sf_pwmdac_pcm_push_tx(pwmdac_dev); + } + + set_current_state(TASK_INTERRUPTIBLE); + } + + pwmdac_dev->tx_thread = NULL; + return 0; +} + +static int sf_pwmdac_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + struct sf_pwmdac_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++; + pwmdac_set(dev); + if (dev->use_pio) { + dev->tx_thread = kthread_create(pwmdac_tx_thread, (void *)dev, "pwmdac"); + if (IS_ERR(dev->tx_thread)) { + return PTR_ERR(dev->tx_thread); + } + wake_up_process(dev->tx_thread); + dev->tx_thread_exit = 0; + } + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + dev->active--; + pwmdac_stop(dev); + if (dev->use_pio) { + if(dev->tx_thread) { + dev->tx_thread_exit = 1; + } + } + break; + default: + ret = -EINVAL; + break; + } + return ret; + + return 0; +} + +static int sf_pwmdac_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct sf_pwmdac_dev *dev = dev_get_drvdata(dai->dev); + + dev->play_dma_data.addr = dev->mapbase + PWMDAC_WDATA; + + switch (params_channels(params)) { + case 2: + dev->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + break; + case 1: + dev->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + break; + default: + dev_err(dai->dev, "%d channels not supported\n", + params_channels(params)); + return -EINVAL; + } + + dev->play_dma_data.fifo_size = 1; + dev->play_dma_data.maxburst = 16; + + snd_soc_dai_init_dma_data(dai, &dev->play_dma_data, NULL); + snd_soc_dai_set_drvdata(dai, dev); + + return 0; +} + +static int sf_pwmdac_clks_get(struct platform_device *pdev, + struct sf_pwmdac_dev *dev) +{ + static struct clk_bulk_data clks[] = { + { .id = "audio_root" }, //clock-names in dts file + { .id = "audio_src" }, + { .id = "audio_12288" }, + { .id = "dma1p_ahb" }, + { .id = "pwmdac_apb" }, + { .id = "dac_mclk" }, + }; + int ret = devm_clk_bulk_get(&pdev->dev, ARRAY_SIZE(clks), clks); + dev->clk_audio_root = clks[0].clk; + dev->clk_audio_src = clks[1].clk; + dev->clk_audio_12288 = clks[2].clk; + dev->clk_dma1p_ahb = clks[3].clk; + dev->clk_pwmdac_apb = clks[4].clk; + dev->clk_dac_mclk = clks[5].clk; + + return ret; +} + +static int sf_pwmdac_resets_get(struct platform_device *pdev, + struct sf_pwmdac_dev *dev) +{ + struct reset_control_bulk_data resets[] = { + { .id = "apb_bus" }, + { .id = "dma1p_ahb" }, + { .id = "apb_pwmdac" }, + }; + int ret = devm_reset_control_bulk_get_exclusive(&pdev->dev, ARRAY_SIZE(resets), resets); + if (ret) + return ret; + + dev->rst_apb_bus = resets[0].rstc; + dev->rst_dma1p_ahb = resets[1].rstc; + dev->rst_apb_pwmdac = resets[2].rstc; + return 0; +} + +static int sf_pwmdac_clk_init(struct platform_device *pdev, + struct sf_pwmdac_dev *dev) +{ + int ret = 0; + + ret = clk_prepare_enable(dev->clk_audio_root); + if (ret) { + dev_err(&pdev->dev, "failed to prepare enable clk_audio_root\n"); + goto err_clk_pwmdac; + } + + ret = clk_prepare_enable(dev->clk_audio_src); + if (ret) { + dev_err(&pdev->dev, "failed to prepare enable clk_audio_src\n"); + goto err_clk_pwmdac; + } + + ret = clk_prepare_enable(dev->clk_audio_12288); + if (ret) { + dev_err(&pdev->dev, "failed to prepare enable clk_audio_12288\n"); + goto err_clk_pwmdac; + } + + ret = clk_prepare_enable(dev->clk_dma1p_ahb); + if (ret) { + dev_err(&pdev->dev, "failed to prepare enable clk_dma1p_ahb\n"); + goto err_clk_pwmdac; + } + + ret = reset_control_deassert(dev->rst_apb_bus); + if (ret) { + printk(KERN_INFO "failed to deassert apb_bus\n"); + goto err_clk_pwmdac; + } + + ret = reset_control_deassert(dev->rst_dma1p_ahb); + if (ret) { + printk(KERN_INFO "failed to deassert dma1p_ahb\n"); + goto err_clk_pwmdac; + } + + ret = clk_set_rate(dev->clk_audio_src, 12288000); + if (ret) { + dev_err(&pdev->dev, "failed to set 12.288 MHz rate for clk_audio_src\n"); + goto err_clk_pwmdac; + } + + ret = reset_control_assert(dev->rst_apb_pwmdac); + if (ret) { + printk(KERN_INFO "failed to assert apb_pwmdac\n"); + goto err_clk_pwmdac; + } + + ret = clk_prepare_enable(dev->clk_dac_mclk); + if (ret) { + dev_err(&pdev->dev, "failed to prepare enable clk_dac_mclk\n"); + goto err_clk_pwmdac; + } + + /* we want 4096kHz but the clock driver always rounds down so add a little slack */ + ret = clk_set_rate(dev->clk_dac_mclk, 4096000 + 64); + if (ret) { + dev_err(&pdev->dev, "failed to set 4096kHz rate for clk_dac_mclk\n"); + goto err_clk_pwmdac; + } + + ret = clk_prepare_enable(dev->clk_pwmdac_apb); + if (ret) { + dev_err(&pdev->dev, "failed to prepare enable clk_pwmdac_apb\n"); + goto err_clk_pwmdac; + } + + ret = reset_control_deassert(dev->rst_apb_pwmdac); + if (ret) { + printk(KERN_INFO "failed to deassert apb_pwmdac\n"); + goto err_clk_pwmdac; + } + printk(KERN_INFO "Initialize pwmdac...success\n"); + +err_clk_pwmdac: + return ret; +} + +static int sf_pwmdac_dai_probe(struct snd_soc_dai *dai) +{ + struct sf_pwmdac_dev *dev = dev_get_drvdata(dai->dev); + + dev->play_dma_data.addr = dev->mapbase + PWMDAC_WDATA; + dev->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + dev->play_dma_data.fifo_size = 1; + dev->play_dma_data.maxburst = 16; + + snd_soc_dai_init_dma_data(dai, &dev->play_dma_data, NULL); + snd_soc_dai_set_drvdata(dai, dev); + + return 0; +} +#define SOC_PWMDAC_ENUM_DECL(xname, xinfo, xget, xput) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = xinfo, .get = xget, \ + .put = xput,} +static const struct snd_kcontrol_new pwmdac_snd_controls[] = { + SOC_PWMDAC_ENUM_DECL("shift_bit", pwmdac_shift_bit_info, + pwmdac_shift_bit_get, pwmdac_shift_bit_put), + SOC_PWMDAC_ENUM_DECL("duty_cycle", pwmdac_duty_cycle_info, + pwmdac_duty_cycle_get, pwmdac_duty_cycle_put), + SOC_PWMDAC_ENUM_DECL("data_mode", pwmdac_data_mode_info, + pwmdac_data_mode_get, pwmdac_data_mode_put), + SOC_PWMDAC_ENUM_DECL("shift", pwmdac_shift_info, + pwmdac_shift_get, pwmdac_shift_put), + SOC_PWMDAC_ENUM_DECL("lr_change", pwmdac_lr_change_info, + pwmdac_lr_change_get, pwmdac_lr_change_put), +}; +static int pwmdac_probe(struct snd_soc_component *component) +{ +// struct sf_pwmdac_dev *priv = snd_soc_component_get_drvdata(component); + snd_soc_add_component_controls(component, pwmdac_snd_controls, + ARRAY_SIZE(pwmdac_snd_controls)); + return 0; +} + + +static const struct snd_soc_dai_ops sf_pwmdac_dai_ops = { + .hw_params = sf_pwmdac_hw_params, + .prepare = sf_pwmdac_prepare, + .trigger = sf_pwmdac_trigger, +}; + +static const struct snd_soc_component_driver sf_pwmdac_component = { + .name = "sf-pwmdac", + .probe = pwmdac_probe, +}; + +static struct snd_soc_dai_driver pwmdac_dai = { + .name = "pwmdac", + .id = 0, + .probe = sf_pwmdac_dai_probe, + .playback = { + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &sf_pwmdac_dai_ops, +}; + +static int sf_pwmdac_probe(struct platform_device *pdev) +{ + struct sf_pwmdac_dev *dev; + struct resource *res; + int ret; + + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + dev->mapbase = res->start; + dev->pwmdac_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(dev->pwmdac_base)) + return PTR_ERR(dev->pwmdac_base); + + ret = sf_pwmdac_clks_get(pdev, dev); + if (ret) { + dev_err(&pdev->dev, "failed to get audio clock\n"); + return ret; + } + + ret = sf_pwmdac_resets_get(pdev, dev); + if (ret) { + dev_err(&pdev->dev, "failed to get audio reset controls\n"); + return ret; + } + + ret = sf_pwmdac_clk_init(pdev, dev); + if (ret) { + dev_err(&pdev->dev, "failed to enable audio clock\n"); + return ret; + } + + dev->dev = &pdev->dev; + dev->mode = shift_8Bit_inverter; + dev->fifo_th = 1;//8byte + pwmdac_config(dev); + + dev->use_pio = false; + dev_set_drvdata(&pdev->dev, dev); + ret = devm_snd_soc_register_component(&pdev->dev, &sf_pwmdac_component, + &pwmdac_dai, 1); + if (ret != 0) { + dev_err(&pdev->dev, "not able to register dai\n"); + return ret; + } + + if (dev->use_pio) { + ret = sf_pwmdac_pcm_register(pdev); + } else { + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, + 0); + } + return 0; +} + + +static int sf_pwmdac_remove(struct platform_device *pdev) +{ + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id sf_pwmdac_of_match[] = { + { .compatible = "starfive,pwmdac", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, sf_pwmdac_of_match); +#endif + + +static struct platform_driver sf_pwmdac_driver = { + .probe = sf_pwmdac_probe, + .remove = sf_pwmdac_remove, + .driver = { + .name = "sf-pwmdac", + .of_match_table = of_match_ptr(sf_pwmdac_of_match), + }, +}; + +module_platform_driver(sf_pwmdac_driver); + +MODULE_AUTHOR("jenny.zhang "); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("starfive pwmdac SoC Interface"); +MODULE_ALIAS("platform:starfive-pwmdac"); diff --git a/sound/soc/starfive/starfive_pwmdac_pcm.c b/sound/soc/starfive/starfive_pwmdac_pcm.c new file mode 100644 index 0000000000000..a31ccfc5cf77f --- /dev/null +++ b/sound/soc/starfive/starfive_pwmdac_pcm.c @@ -0,0 +1,234 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 StarFive Technology Co., Ltd. + */ +#include +#include +#include +#include +#include "pwmdac.h" + +#define BUFFER_BYTES_MAX (3 * 2 * 8 * PERIOD_BYTES_MIN) +#define PERIOD_BYTES_MIN 4096 +#define PERIODS_MIN 2 + +static unsigned int sf_pwmdac_pcm_tx_8(struct sf_pwmdac_dev *dev, + struct snd_pcm_runtime *runtime, unsigned int tx_ptr, + bool *period_elapsed) +{ + const u8 (*p)[2] = (void *)runtime->dma_area; + unsigned int period_pos = tx_ptr % runtime->period_size; + int i; + u32 basedat = 0; + + for (i = 0; i < dev->fifo_th; i++) { + basedat = (p[tx_ptr][0]<<8)|(p[tx_ptr][1] << 24); + iowrite32(basedat,dev->pwmdac_base + PWMDAC_WDATA); + period_pos++; + if (++tx_ptr >= runtime->buffer_size) + tx_ptr = 0; + } + + *period_elapsed = period_pos >= runtime->period_size; + + return tx_ptr; +} + + +static unsigned int sf_pwmdac_pcm_tx_16(struct sf_pwmdac_dev *dev, + struct snd_pcm_runtime *runtime, unsigned int tx_ptr, + bool *period_elapsed) +{ + const u16 (*p)[2] = (void *)runtime->dma_area; + unsigned int period_pos = tx_ptr % runtime->period_size; + int i; + u32 basedat = 0; + + for (i = 0; i < dev->fifo_th; i++) { + basedat = (p[tx_ptr][0])|(p[tx_ptr][1] << 16); + iowrite32(basedat,dev->pwmdac_base + PWMDAC_WDATA); + period_pos++; + if (++tx_ptr >= runtime->buffer_size) + tx_ptr = 0; + } + + *period_elapsed = period_pos >= runtime->period_size; + return tx_ptr; +} + +static const struct snd_pcm_hardware sf_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_16000, + .rate_min = 16000, + .rate_max = 16000, + .formats = SNDRV_PCM_FMTBIT_S16_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 = 2, +}; + +static void sf_pcm_transfer(struct sf_pwmdac_dev *dev, bool push) +{ + struct snd_pcm_substream *substream; + bool active, period_elapsed; + + rcu_read_lock(); + if (push) + substream = rcu_dereference(dev->tx_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); + } + + if (period_elapsed) + snd_pcm_period_elapsed(substream); + } + rcu_read_unlock(); +} + +void sf_pwmdac_pcm_push_tx(struct sf_pwmdac_dev *dev) +{ + sf_pcm_transfer(dev, true); +} + + +static int sf_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 sf_pwmdac_dev *dev = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); + + snd_soc_set_runtime_hwparams(substream, &sf_pcm_hardware); + snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); + runtime->private_data = dev; + + return 0; +} + + +static int sf_pcm_close(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + synchronize_rcu(); + return 0; +} + +static int sf_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 sf_pwmdac_dev *dev = runtime->private_data; + int ret; + + + 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_U8: + case SNDRV_PCM_FORMAT_S8: + dev->tx_fn = sf_pwmdac_pcm_tx_8; + break; + case SNDRV_PCM_FORMAT_S16_LE: + dev->tx_fn = sf_pwmdac_pcm_tx_16; + break; + default: + dev_err(dev->dev, "invalid format\n"); + return -EINVAL; + } + + return 0; +} + + +static int sf_pcm_trigger(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int cmd) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct sf_pwmdac_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); + } + 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); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static snd_pcm_uframes_t sf_pcm_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct sf_pwmdac_dev *dev = runtime->private_data; + snd_pcm_uframes_t pos; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + pos = READ_ONCE(dev->tx_ptr); + + return pos < runtime->buffer_size ? pos : 0; +} + +static int sf_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) +{ + size_t size = sf_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 = sf_pcm_open, + .close = sf_pcm_close, + .hw_params = sf_pcm_hw_params, + .trigger = sf_pcm_trigger, + .pointer = sf_pcm_pointer, + .pcm_construct = sf_pcm_new, +}; + +int sf_pwmdac_pcm_register(struct platform_device *pdev) +{ + return devm_snd_soc_register_component(&pdev->dev, &dw_pcm_component, + NULL, 0); +} diff --git a/sound/soc/starfive/starfive_pwmdac_transmitter.c b/sound/soc/starfive/starfive_pwmdac_transmitter.c new file mode 100644 index 0000000000000..c6e756ff135bf --- /dev/null +++ b/sound/soc/starfive/starfive_pwmdac_transmitter.c @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 StarFive Technology Co., Ltd. + */ +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "pwmdac-dit" + +#define STUB_RATES SNDRV_PCM_RATE_8000_192000 +#define STUB_FORMATS (SNDRV_PCM_FMTBIT_S8|\ + SNDRV_PCM_FMTBIT_U8 |\ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dapm_widget dit_widgets[] = { + SND_SOC_DAPM_OUTPUT("pwmdac-out"), +}; + +static const struct snd_soc_dapm_route dit_routes[] = { + { "pwmdac-out", NULL, "Playback" }, +}; + +static struct snd_soc_component_driver soc_codec_pwmdac_dit = { + .dapm_widgets = dit_widgets, + .num_dapm_widgets = ARRAY_SIZE(dit_widgets), + .dapm_routes = dit_routes, + .num_dapm_routes = ARRAY_SIZE(dit_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, + .non_legacy_dai_naming = 1, +}; + +static struct snd_soc_dai_driver dit_stub_dai = { + .name = "pwmdac-dit-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 384, + .rates = STUB_RATES, + .formats = STUB_FORMATS, + }, +}; + +static int pwmdac_dit_probe(struct platform_device *pdev) +{ + + return devm_snd_soc_register_component(&pdev->dev, + &soc_codec_pwmdac_dit, + &dit_stub_dai, 1); +} + +#ifdef CONFIG_OF +static const struct of_device_id pwmdac_dit_dt_ids[] = { + { .compatible = "linux,pwmdac-dit", }, + { } +}; +MODULE_DEVICE_TABLE(of, pwmdac_dit_dt_ids); +#endif + +static struct platform_driver pwmdac_dit_driver = { + .probe = pwmdac_dit_probe, + .driver = { + .name = DRV_NAME, + .of_match_table = of_match_ptr(pwmdac_dit_dt_ids), + }, +}; + +module_platform_driver(pwmdac_dit_driver); + +MODULE_AUTHOR("jenny.zhang "); +MODULE_DESCRIPTION("pwmdac dummy codec driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform: starfive-pwmdac dummy codec"); diff --git a/sound/soc/starfive/starfive_spdif.c b/sound/soc/starfive/starfive_spdif.c new file mode 100644 index 0000000000000..0767527065473 --- /dev/null +++ b/sound/soc/starfive/starfive_spdif.c @@ -0,0 +1,383 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 StarFive Technology Co., Ltd. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include "starfive_spdif.h" + +static irqreturn_t spdif_irq_handler(int irq, void *dev_id) +{ + struct sf_spdif_dev *dev = dev_id; + bool irq_valid = false; + unsigned int intr; + unsigned int stat; + + regmap_read(dev->regmap, SPDIF_INT_REG, &intr); + regmap_read(dev->regmap, SPDIF_STAT_REG, &stat); + regmap_update_bits(dev->regmap, SPDIF_CTRL, + SPDIF_MASK_ENABLE, 0); + regmap_update_bits(dev->regmap, SPDIF_INT_REG, + SPDIF_INT_REG_BIT, 0); + + if ((stat & SPDIF_EMPTY_FLAG) || (stat & SPDIF_AEMPTY_FLAG)) { + sf_spdif_pcm_push_tx(dev); + irq_valid = true; + } + + if ((stat & SPDIF_FULL_FLAG) || (stat & SPDIF_AFULL_FLAG)) { + sf_spdif_pcm_pop_rx(dev); + irq_valid = true; + } + + if (stat & SPDIF_PARITY_FLAG) { + irq_valid = true; + } + + if (stat & SPDIF_UNDERR_FLAG) { + irq_valid = true; + } + + if (stat & SPDIF_OVRERR_FLAG) { + irq_valid = true; + } + + if (stat & SPDIF_SYNCERR_FLAG) { + irq_valid = true; + } + + if (stat & SPDIF_LOCK_FLAG) { + irq_valid = true; + } + + if (stat & SPDIF_BEGIN_FLAG) { + irq_valid = true; + } + + if (stat & SPDIF_RIGHT_LEFT) { + irq_valid = true; + } + + regmap_update_bits(dev->regmap, SPDIF_CTRL, + SPDIF_MASK_ENABLE, SPDIF_MASK_ENABLE); + + if (irq_valid) + return IRQ_HANDLED; + else + return IRQ_NONE; +} + +static int sf_spdif_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct sf_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai); + bool tx; + + tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + if (tx) { + /* tx mode */ + regmap_update_bits(spdif->regmap, SPDIF_CTRL, + SPDIF_TR_MODE, SPDIF_TR_MODE); + + regmap_update_bits(spdif->regmap, SPDIF_CTRL, + SPDIF_MASK_FIFO, SPDIF_EMPTY_MASK | SPDIF_AEMPTY_MASK); + } else { + /* rx mode */ + regmap_update_bits(spdif->regmap, SPDIF_CTRL, + SPDIF_TR_MODE, 0); + + regmap_update_bits(spdif->regmap, SPDIF_CTRL, + SPDIF_MASK_FIFO, SPDIF_FULL_MASK | SPDIF_AFULL_MASK); + } + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + /* clock recovery form the SPDIF data stream 0:clk_enable */ + regmap_update_bits(spdif->regmap, SPDIF_CTRL, + SPDIF_CLK_ENABLE, 0); + + regmap_update_bits(spdif->regmap, SPDIF_CTRL, + SPDIF_ENABLE, SPDIF_ENABLE); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + /* clock recovery form the SPDIF data stream 1:power save mode */ + regmap_update_bits(spdif->regmap, SPDIF_CTRL, + SPDIF_CLK_ENABLE, SPDIF_CLK_ENABLE); + + regmap_update_bits(spdif->regmap, SPDIF_CTRL, + SPDIF_ENABLE, 0); + break; + default: + printk(KERN_ERR "%s L.%d cmd:%d\n", __func__, __LINE__, cmd); + return -EINVAL; + } + + return 0; +} + +static int sf_spdif_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct sf_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai); + unsigned int channels; + unsigned int rate; + unsigned int format; + unsigned int tsamplerate; + + channels = params_channels(params); + rate = params_rate(params); + format = params_format(params); + + switch (channels) { + case 2: + break; + default: + dev_err(dai->dev, "invalid channels number\n"); + return -EINVAL; + } + + switch (format) { + case SNDRV_PCM_FORMAT_S16_LE: + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S32_LE: + break; + default: + dev_err(spdif->dev, "invalid format\n"); + return -EINVAL; + } + + switch (rate) { + case 8000: + case 11025: + case 16000: + case 22050: + break; + default: + printk(KERN_ERR "channel:%d sample rate:%d\n", channels, rate); + return -EINVAL; + } + + /* 12288000/128=96000 */ + tsamplerate = (96000 + rate/2)/rate - 1; + + if (rate < 3) { + return -EINVAL; + } + + /* transmission sample rate */ + regmap_update_bits(spdif->regmap, SPDIF_CTRL, 0xFF, tsamplerate); + + return 0; +} + +static int sf_spdif_dai_probe(struct snd_soc_dai *dai) +{ + struct sf_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai); + + #if 0 + spdif->play_dma_data.addr = (dma_addr_t)spdif->spdif_base + SPDIF_FIFO_ADDR; + spdif->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + spdif->play_dma_data.fifo_size = 16; + spdif->play_dma_data.maxburst = 16; + spdif->capture_dma_data.addr = (dma_addr_t)spdif->spdif_base + SPDIF_FIFO_ADDR; + spdif->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + spdif->capture_dma_data.fifo_size = 16; + spdif->capture_dma_data.maxburst = 16; + snd_soc_dai_init_dma_data(dai, &spdif->play_dma_data, &spdif->capture_dma_data); + snd_soc_dai_set_drvdata(dai, spdif); + #endif + + /* reset */ + regmap_update_bits(spdif->regmap, SPDIF_CTRL, + SPDIF_ENABLE | SPDIF_SFR_ENABLE | SPDIF_FIFO_ENABLE, 0); + + /* clear irq */ + regmap_update_bits(spdif->regmap, SPDIF_INT_REG, + SPDIF_INT_REG_BIT, 0); + + /* power save mode */ + regmap_update_bits(spdif->regmap, SPDIF_CTRL, + SPDIF_CLK_ENABLE, SPDIF_CLK_ENABLE); + + /* power save mode */ + regmap_update_bits(spdif->regmap, SPDIF_CTRL, + SPDIF_CLK_ENABLE, SPDIF_CLK_ENABLE); + + regmap_update_bits(spdif->regmap, SPDIF_CTRL, + SPDIF_PARITCHECK|SPDIF_VALIDITYCHECK|SPDIF_DUPLICATE, + SPDIF_PARITCHECK|SPDIF_VALIDITYCHECK|SPDIF_DUPLICATE); + + regmap_update_bits(spdif->regmap, SPDIF_CTRL, + SPDIF_SETPREAMBB, SPDIF_SETPREAMBB); + + regmap_update_bits(spdif->regmap, SPDIF_INT_REG, + 0x1FFF<regmap, SPDIF_FIFO_CTRL, + 0xFFFFFFFF, 0x20|(0x20<regmap, SPDIF_CTRL, + SPDIF_PARITYGEN, SPDIF_PARITYGEN); + + regmap_update_bits(spdif->regmap, SPDIF_CTRL, + SPDIF_MASK_ENABLE, SPDIF_MASK_ENABLE); + + /* APB access to FIFO enable, disable if use DMA/FIFO */ + regmap_update_bits(spdif->regmap, SPDIF_CTRL, + SPDIF_USE_FIFO_IF, 0); + + /* two channel */ + regmap_update_bits(spdif->regmap, SPDIF_CTRL, + SPDIF_CHANNEL_MODE, 0); + + return 0; +} + +static const struct snd_soc_dai_ops sf_spdif_dai_ops = { + .trigger = sf_spdif_trigger, + .hw_params = sf_spdif_hw_params, +}; + +#define SF_PCM_RATE_44100_192000 (SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | \ + SNDRV_PCM_RATE_96000 | \ + SNDRV_PCM_RATE_192000) + +#define SF_PCM_RATE_8000_22050 (SNDRV_PCM_RATE_8000 | \ + SNDRV_PCM_RATE_11025 | \ + SNDRV_PCM_RATE_16000 | \ + SNDRV_PCM_RATE_22050) + +static struct snd_soc_dai_driver sf_spdif_dai = { + .name = "spdif", + .id = 0, + .probe = sf_spdif_dai_probe, + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SF_PCM_RATE_8000_22050, + .formats = SNDRV_PCM_FMTBIT_S16_LE \ + |SNDRV_PCM_FMTBIT_S24_LE \ + |SNDRV_PCM_FMTBIT_S32_LE, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SF_PCM_RATE_8000_22050, + .formats = SNDRV_PCM_FMTBIT_S16_LE \ + |SNDRV_PCM_FMTBIT_S24_LE \ + |SNDRV_PCM_FMTBIT_S32_LE, + }, + .ops = &sf_spdif_dai_ops, + .symmetric_rate = 1, +}; + +static const struct snd_soc_component_driver sf_spdif_component = { + .name = "sf-spdif", +}; + +static const struct regmap_config sf_spdif_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x200, +}; + +static int sf_spdif_probe(struct platform_device *pdev) +{ + struct sf_spdif_dev *spdif; + struct resource *res; + void __iomem *base; + int ret; + int irq; + + spdif = devm_kzalloc(&pdev->dev, sizeof(*spdif), GFP_KERNEL); + if (!spdif) + return -ENOMEM; + + platform_set_drvdata(pdev, spdif); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + spdif->spdif_base = base; + spdif->regmap = devm_regmap_init_mmio(&pdev->dev, spdif->spdif_base, + &sf_spdif_regmap_config); + if (IS_ERR(spdif->regmap)) + return PTR_ERR(spdif->regmap); + + spdif->dev = &pdev->dev; + spdif->fifo_th = 16; + + irq = platform_get_irq(pdev, 0); + if (irq >= 0) { + ret = devm_request_irq(&pdev->dev, irq, spdif_irq_handler, 0, + pdev->name, spdif); + if (ret < 0) { + dev_err(&pdev->dev, "failed to request irq\n"); + return ret; + } + } + + ret = devm_snd_soc_register_component(&pdev->dev, &sf_spdif_component, + &sf_spdif_dai, 1); + if (ret) + goto err_clk_disable; + + if (irq >= 0) { + ret = sf_spdif_pcm_register(pdev); + spdif->use_pio = true; + } else { + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, + 0); + spdif->use_pio = false; + } + + if (ret) + goto err_clk_disable; + + return 0; + +err_clk_disable: + return ret; +} + +static const struct of_device_id sf_spdif_of_match[] = { + { .compatible = "starfive,sf-spdif", }, + {}, +}; +MODULE_DEVICE_TABLE(of, sf_spdif_of_match); + +static struct platform_driver sf_spdif_driver = { + .driver = { + .name = "sf-spdif", + .of_match_table = sf_spdif_of_match, + }, + .probe = sf_spdif_probe, +}; +module_platform_driver(sf_spdif_driver); + +MODULE_AUTHOR("michael.yan "); +MODULE_DESCRIPTION("starfive SPDIF driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/starfive/starfive_spdif.h b/sound/soc/starfive/starfive_spdif.h new file mode 100644 index 0000000000000..5d27ad632d589 --- /dev/null +++ b/sound/soc/starfive/starfive_spdif.h @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 StarFive Technology Co., Ltd. + */ +#ifndef __SND_SOC_STARFIVE_SPDIF_H +#define __SND_SOC_STARFIVE_SPDIF_H + +#include +#include +#include +#include +#include +#include +#include + +#define SPDIF_CTRL (0x0) +#define SPDIF_INT_REG (0x4) +#define SPDIF_FIFO_CTRL (0x8) +#define SPDIF_STAT_REG (0xC) + +#define SPDIF_FIFO_ADDR (0x100) +#define DMAC_SPDIF_POLLING_LEN (256) + +///ctrl: sampled on the rising clock edge +#define SPDIF_TSAMPLERATE 0///[SRATEW-1:0] +#define SPDIF_SFR_ENABLE (1<<8) ///0:SFR reg reset to defualt value; auto set back to '1' after reset +#define SPDIF_ENABLE (1<<9) ///0:reset of SPDIF block, SRF bits are unchanged; 1:enables SPDIF module +#define SPDIF_FIFO_ENABLE (1<<10) ///0:FIFO pointers are reset to zero,threshold levels for FIFO are unchaned; auto set back to '1' +#define SPDIF_CLK_ENABLE (1<<11) ///1:blocked and the modules are in power save mode; 0:block feeds the modules +#define SPDIF_TR_MODE (1<<12) ///0:rx; 1:tx +#define SPDIF_PARITCHECK (1<<13) ///0:party bit rx in a sub-frame is repeated on the parity; 1:check on a parity error +#define SPDIF_PARITYGEN (1<<14) ///0:parity bit from FIFO is transmitted in sub-frame;1:parity bit generated inside the core and added to a transmitted sub-frame +#define SPDIF_VALIDITYCHECK (1<<15) ///0:validity bit in frame isn't checked and all frame are written; 1:validity bit rx is checked +#define SPDIF_CHANNEL_MODE (1<<16) ///0:two-channel; 1:single-channel +#define SPDIF_DUPLICATE (1<<17) ///only tx -single-channel mode; 0:secondary channel; 1: left(primary) channel +#define SPDIF_SETPREAMBB (1<<18) ///only tx; 0:first preamble B after reset tx valid sub-frame; 1:first preamble B is tx after preambleddel(INT_REG) +#define SPDIF_USE_FIFO_IF (1<<19) ///0:FIFO disabled ,APB accese FIFO; 1:FIFO enable, APB access to FIFO disable; +///#define RESERVED (1<<20) +#define SPDIF_PARITY_MASK (1<<21) +#define SPDIF_UNDERR_MASK (1<<22) +#define SPDIF_OVRERR_MASK (1<<23) +#define SPDIF_EMPTY_MASK (1<<24) +#define SPDIF_AEMPTY_MASK (1<<25) +#define SPDIF_FULL_MASK (1<<26) +#define SPDIF_AFULL_MASK (1<<27) +#define SPDIF_SYNCERR_MASK (1<<28) +#define SPDIF_LOCK_MASK (1<<29) +#define SPDIF_BEGIN_MASK (1<<30) +#define SPDIF_INTEREQ_MAKS (1<<31) + +#define SPDIF_MASK_ENABLE (SPDIF_PARITY_MASK | SPDIF_UNDERR_MASK | SPDIF_OVRERR_MASK | SPDIF_EMPTY_MASK | \ + SPDIF_AEMPTY_MASK | SPDIF_FULL_MASK | SPDIF_AFULL_MASK | SPDIF_SYNCERR_MASK | \ + SPDIF_LOCK_MASK | SPDIF_BEGIN_MASK | SPDIF_INTEREQ_MAKS) + +#define SPDIF_MASK_FIFO (SPDIF_EMPTY_MASK | SPDIF_AEMPTY_MASK | SPDIF_FULL_MASK | SPDIF_AFULL_MASK) + +////INT_REG +#define SPDIF_RSAMPLERATE 0 ///[SRATEW-1:0] +#define SPDIF_PREAMBLEDEL 8 ///[PDELAYW+7:8] first B delay +#define SPDIF_PARITYO (1<<21) ///0:clear parity error +#define SPDIF_TDATA_UNDERR (1<<22) ///tx data underrun error;0:clear +#define SPDIF_RDATA_OVRERR (1<<23) ///rx data overrun error; 0:clear +#define SPDIF_FIFO_EMPTY (1<<24) ///empty; 0:clear +#define SPDIF_FIOF_AEMPTY (1<<25) ///almost empty; 0:clear +#define SPDIF_FIFO_FULL (1<<26) ///FIFO full; 0:clear +#define SPDIF_FIFO_AFULL (1<<27) ///FIFO almost full; 0:clear +#define SPDIF_SYNCERR (1<<28) ///sync error; 0:clear +#define SPDIF_LOCK (1<<29) ///sync; 0:clear +#define SPDIF_BLOCK_BEGIN (1<<30) ///new start block rx data + +#define SPDIF_INT_REG_BIT (SPDIF_PARITYO | SPDIF_TDATA_UNDERR | SPDIF_RDATA_OVRERR | SPDIF_FIFO_EMPTY | \ + SPDIF_FIOF_AEMPTY | SPDIF_FIFO_FULL | SPDIF_FIFO_AFULL | SPDIF_SYNCERR | \ + SPDIF_LOCK | SPDIF_BLOCK_BEGIN) + +#define SPDIF_ERROR_INT_STATUS (SPDIF_PARITYO | SPDIF_TDATA_UNDERR | SPDIF_RDATA_OVRERR) +#define SPDIF_FIFO_INT_STATUS (SPDIF_FIFO_EMPTY | SPDIF_FIOF_AEMPTY | SPDIF_FIFO_FULL | SPDIF_FIFO_AFULL) + +#define SPDIF_INT_PARITY_ERROR (-1) +#define SPDIF_INT_TDATA_UNDERR (-2) +#define SPDIF_INT_RDATA_OVRERR (-3) +#define SPDIF_INT_FIFO_EMPTY 1 +#define SPDIF_INT_FIFO_AEMPTY 2 +#define SPDIF_INT_FIFO_FULL 3 +#define SPDIF_INT_FIFO_AFULL 4 +#define SPDIF_INT_SYNCERR (-4) +#define SPDIF_INT_LOCK 5 ///reciever has become synchronized with input data stream +#define SPDIF_INT_BLOCK_BEGIN 6 ///start a new block in recieve data, written into FIFO + +///FIFO_CTRL +#define SPDIF_AEMPTY_THRESHOLD 0///[depth-1:0] +#define SPDIF_AFULL_THRESHOLD 16///[depth+15:16] + +///STAT_REG +#define SPDIF_FIFO_LEVEL (1<<0) +#define SPDIF_PARITY_FLAG (1<<21) ///1:error; 0:repeated +#define SPDIF_UNDERR_FLAG (1<<22) ///1:error +#define SPDIF_OVRERR_FLAG (1<<23) ///1:error +#define SPDIF_EMPTY_FLAG (1<<24) ///1:fifo empty +#define SPDIF_AEMPTY_FLAG (1<<25) ///1:fifo almost empty +#define SPDIF_FULL_FLAG (1<<26) ///1:fifo full +#define SPDIF_AFULL_FLAG (1<<27) ///1:fifo almost full +#define SPDIF_SYNCERR_FLAG (1<<28) ///1:rx sync error +#define SPDIF_LOCK_FLAG (1<<29) ///1:RX sync +#define SPDIF_BEGIN_FLAG (1<<30) ///1:start a new block +#define SPDIF_RIGHT_LEFT (1<<31) ///1:left channel received and tx into FIFO; 0:right channel received and tx into FIFO + +#define SPDIF_STAT (SPDIF_PARITY_FLAG | SPDIF_UNDERR_FLAG | SPDIF_OVRERR_FLAG | SPDIF_EMPTY_FLAG | \ + SPDIF_AEMPTY_FLAG | SPDIF_FULL_FLAG | SPDIF_AFULL_FLAG | SPDIF_SYNCERR_FLAG | \ + SPDIF_LOCK_FLAG | SPDIF_BEGIN_FLAG | SPDIF_RIGHT_LEFT) +struct sf_spdif_dev { + void __iomem *spdif_base; + struct regmap *regmap; + struct device *dev; + u32 fifo_th; + int active; + + /* data related to DMA transfers b/w i2s and DMAC */ + struct snd_dmaengine_dai_dma_data play_dma_data; + struct snd_dmaengine_dai_dma_data capture_dma_data; + + bool use_pio; + struct snd_pcm_substream __rcu *tx_substream; + struct snd_pcm_substream __rcu *rx_substream; + + unsigned int (*tx_fn)(struct sf_spdif_dev *dev, + struct snd_pcm_runtime *runtime, unsigned int tx_ptr, + bool *period_elapsed, snd_pcm_format_t format); + unsigned int (*rx_fn)(struct sf_spdif_dev *dev, + struct snd_pcm_runtime *runtime, unsigned int rx_ptr, + bool *period_elapsed, snd_pcm_format_t format); + + snd_pcm_format_t format; + //unsigned int sample_bits; + unsigned int tx_ptr; + unsigned int rx_ptr; + + struct snd_dmaengine_dai_dma_data dma_data; +}; + +#if IS_ENABLED(CONFIG_SND_STARFIVE_SPDIF_PCM) +void sf_spdif_pcm_push_tx(struct sf_spdif_dev *dev); +void sf_spdif_pcm_pop_rx(struct sf_spdif_dev *dev); +int sf_spdif_pcm_register(struct platform_device *pdev); +#else +void sf_spdif_pcm_push_tx(struct sf_spdif_dev *dev) { } +void sf_spdif_pcm_pop_rx(struct sf_spdif_dev *dev) { } +int sf_spdif_pcm_register(struct platform_device *pdev) +{ + return -EINVAL; +} +#endif + + +#endif /* __SND_SOC_STARFIVE_SPDIF_H */ diff --git a/sound/soc/starfive/starfive_spdif_pcm.c b/sound/soc/starfive/starfive_spdif_pcm.c new file mode 100644 index 0000000000000..81e1bc9387dcd --- /dev/null +++ b/sound/soc/starfive/starfive_spdif_pcm.c @@ -0,0 +1,288 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 StarFive Technology Co., Ltd. + */ +#include +#include +#include +#include +#include "starfive_spdif.h" + +#define BUFFER_BYTES_MAX (3 * 2 * 8 * PERIOD_BYTES_MIN) +#define PERIOD_BYTES_MIN 4096 +#define PERIODS_MIN 2 + +static unsigned int sf_spdif_pcm_tx(struct sf_spdif_dev *dev, + struct snd_pcm_runtime *runtime, unsigned int tx_ptr, + bool *period_elapsed, snd_pcm_format_t format) +{ + const u16 (*p16)[2] = (void *)runtime->dma_area; + const u32 (*p32)[2] = (void *)runtime->dma_area; + u32 data[2]; + unsigned int period_pos = tx_ptr % runtime->period_size; + int i; + + for (i = 0; i < dev->fifo_th; i++) { + if (SNDRV_PCM_FORMAT_S16_LE == format) { + data[0] = p16[tx_ptr][0]; + data[1] = p16[tx_ptr][1]; + data[0] = data[0]<<8; + data[1] = data[1]<<8; + } else if (SNDRV_PCM_FORMAT_S24_LE == format) { + data[0] = p32[tx_ptr][0]; + data[1] = p32[tx_ptr][1]; + } else if (SNDRV_PCM_FORMAT_S32_LE == format) { + data[0] = p32[tx_ptr][0]; + data[1] = p32[tx_ptr][1]; + data[0] = data[0]>>8; + data[1] = data[1]>>8; + } + + iowrite32(data[0], dev->spdif_base + SPDIF_FIFO_ADDR); + iowrite32(data[1], dev->spdif_base + SPDIF_FIFO_ADDR); + period_pos++; + if (++tx_ptr >= runtime->buffer_size) { + tx_ptr = 0; + } + } + + *period_elapsed = period_pos >= runtime->period_size; + return tx_ptr; +} + +static unsigned int sf_spdif_pcm_rx(struct sf_spdif_dev *dev, + struct snd_pcm_runtime *runtime, unsigned int rx_ptr, + bool *period_elapsed, snd_pcm_format_t format) +{ + u16 (*p16)[2] = (void *)runtime->dma_area; + u32 (*p32)[2] = (void *)runtime->dma_area; + u32 data[2]; + unsigned int period_pos = rx_ptr % runtime->period_size; + int i; + + for (i = 0; i < dev->fifo_th; i++) { + data[0] = ioread32(dev->spdif_base + SPDIF_FIFO_ADDR); + data[1] = ioread32(dev->spdif_base + SPDIF_FIFO_ADDR); + if (SNDRV_PCM_FORMAT_S16_LE == format) { + p16[rx_ptr][0] = data[0]>>8; + p16[rx_ptr][1] = data[1]>>8; + } else if (SNDRV_PCM_FORMAT_S24_LE == format) { + p32[rx_ptr][0] = data[0]; + p32[rx_ptr][1] = data[1]; + } else if (SNDRV_PCM_FORMAT_S32_LE == format) { + p32[rx_ptr][0] = data[0]<<8; + p32[rx_ptr][1] = data[1]<<8; + } + + period_pos++; + if (++rx_ptr >= runtime->buffer_size) + rx_ptr = 0; + } + + *period_elapsed = period_pos >= runtime->period_size; + return rx_ptr; +} + +static const struct snd_pcm_hardware sf_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_8000 | + SNDRV_PCM_RATE_11025 | + SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_22050 | + SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000, + .rate_min = 8000, + .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 sf_spdif_pcm_transfer(struct sf_spdif_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, dev->format); + 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, dev->format); + cmpxchg(&dev->rx_ptr, ptr, new_ptr); + } + + if (period_elapsed) + snd_pcm_period_elapsed(substream); + } + rcu_read_unlock(); +} + +void sf_spdif_pcm_push_tx(struct sf_spdif_dev *dev) +{ + sf_spdif_pcm_transfer(dev, true); +} + +void sf_spdif_pcm_pop_rx(struct sf_spdif_dev *dev) +{ + sf_spdif_pcm_transfer(dev, false); +} + +static int sf_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 sf_spdif_dev *dev = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); + + snd_soc_set_runtime_hwparams(substream, &sf_pcm_hardware); + snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); + runtime->private_data = dev; + + return 0; +} + +static int sf_pcm_close(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + synchronize_rcu(); + return 0; +} + +static int sf_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 sf_spdif_dev *dev = runtime->private_data; + int ret; + + switch (params_channels(hw_params)) { + case 2: + break; + default: + dev_err(dev->dev, "invalid channels number\n"); + return -EINVAL; + } + + dev->format = params_format(hw_params); + switch (dev->format) { + case SNDRV_PCM_FORMAT_S16_LE: + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S32_LE: + break; + default: + dev_err(dev->dev, "invalid format\n"); + return -EINVAL; + } + + dev->tx_fn = sf_spdif_pcm_tx; + dev->rx_fn = sf_spdif_pcm_rx; + + return 0; +} + +static int sf_pcm_trigger(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int cmd) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct sf_spdif_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 sf_pcm_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct sf_spdif_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 sf_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) +{ + size_t size = sf_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 sf_pcm_component = { + .open = sf_pcm_open, + .close = sf_pcm_close, + .hw_params = sf_pcm_hw_params, + .trigger = sf_pcm_trigger, + .pointer = sf_pcm_pointer, + .pcm_construct = sf_pcm_new, +}; + +int sf_spdif_pcm_register(struct platform_device *pdev) +{ + return devm_snd_soc_register_component(&pdev->dev, &sf_pcm_component, + NULL, 0); +} + From d0d3267d416e3725a7af7778b8e8089580fabda6 Mon Sep 17 00:00:00 2001 From: "sw.multimedia" Date: Tue, 31 Aug 2021 16:48:57 +0800 Subject: [PATCH 64/80] drm/starfive: Add StarFive drm driver 1. Add starfive DRM Display driver framework 2. Support M31 Phy and tda998x Signed-off-by: jack.zhu Signed-off-by: keith.zhao Signed-off-by: Emil Renner Berthing --- drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/starfive/Kconfig | 16 + drivers/gpu/drm/starfive/Makefile | 13 + drivers/gpu/drm/starfive/README.txt | 56 ++ drivers/gpu/drm/starfive/starfive_drm_crtc.c | 520 +++++++++++ drivers/gpu/drm/starfive/starfive_drm_crtc.h | 82 ++ drivers/gpu/drm/starfive/starfive_drm_drv.c | 268 ++++++ drivers/gpu/drm/starfive/starfive_drm_drv.h | 25 + .../gpu/drm/starfive/starfive_drm_encoder.c | 130 +++ .../gpu/drm/starfive/starfive_drm_encoder.h | 17 + drivers/gpu/drm/starfive/starfive_drm_gem.c | 347 ++++++++ drivers/gpu/drm/starfive/starfive_drm_gem.h | 39 + drivers/gpu/drm/starfive/starfive_drm_lcdc.c | 520 +++++++++++ drivers/gpu/drm/starfive/starfive_drm_lcdc.h | 160 ++++ drivers/gpu/drm/starfive/starfive_drm_plane.c | 226 +++++ drivers/gpu/drm/starfive/starfive_drm_plane.h | 12 + drivers/gpu/drm/starfive/starfive_drm_vpp.c | 822 ++++++++++++++++++ drivers/gpu/drm/starfive/starfive_drm_vpp.h | 213 +++++ 19 files changed, 3469 insertions(+) create mode 100644 drivers/gpu/drm/starfive/Kconfig create mode 100644 drivers/gpu/drm/starfive/Makefile create mode 100644 drivers/gpu/drm/starfive/README.txt create mode 100644 drivers/gpu/drm/starfive/starfive_drm_crtc.c create mode 100644 drivers/gpu/drm/starfive/starfive_drm_crtc.h create mode 100644 drivers/gpu/drm/starfive/starfive_drm_drv.c create mode 100644 drivers/gpu/drm/starfive/starfive_drm_drv.h create mode 100644 drivers/gpu/drm/starfive/starfive_drm_encoder.c create mode 100644 drivers/gpu/drm/starfive/starfive_drm_encoder.h create mode 100644 drivers/gpu/drm/starfive/starfive_drm_gem.c create mode 100644 drivers/gpu/drm/starfive/starfive_drm_gem.h create mode 100644 drivers/gpu/drm/starfive/starfive_drm_lcdc.c create mode 100644 drivers/gpu/drm/starfive/starfive_drm_lcdc.h create mode 100644 drivers/gpu/drm/starfive/starfive_drm_plane.c create mode 100644 drivers/gpu/drm/starfive/starfive_drm_plane.h create mode 100644 drivers/gpu/drm/starfive/starfive_drm_vpp.c create mode 100644 drivers/gpu/drm/starfive/starfive_drm_vpp.h diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 0039df26854ba..3fb22d15ceafb 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -330,6 +330,8 @@ source "drivers/gpu/drm/shmobile/Kconfig" source "drivers/gpu/drm/sun4i/Kconfig" +source "drivers/gpu/drm/starfive/Kconfig" + source "drivers/gpu/drm/omapdrm/Kconfig" source "drivers/gpu/drm/tilcdc/Kconfig" diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 0dff40bb863c3..a5f54b40d75a7 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -96,6 +96,7 @@ obj-y += rcar-du/ obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/ obj-y += omapdrm/ obj-$(CONFIG_DRM_SUN4I) += sun4i/ +obj-$(CONFIG_DRM_STARFIVE) += starfive/ obj-y += tilcdc/ obj-$(CONFIG_DRM_QXL) += qxl/ obj-$(CONFIG_DRM_VIRTIO_GPU) += virtio/ diff --git a/drivers/gpu/drm/starfive/Kconfig b/drivers/gpu/drm/starfive/Kconfig new file mode 100644 index 0000000000000..4d5f1f5972c48 --- /dev/null +++ b/drivers/gpu/drm/starfive/Kconfig @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2021 StarFive Technology Co., Ltd. + +config DRM_STARFIVE + tristate "DRM Support for StarFive SoCs" + depends on DRM + depends on SOC_STARFIVE || COMPILE_TEST + select DRM_GEM_CMA_HELPER + select DRM_KMS_HELPER + select DRM_MIPI_DSI + select DRM_PANEL + help + Choose this option if you have a StarFive SoCs. + The module will be called starfive-drm + This driver provides kernel mode setting and + buffer management to userspace. diff --git a/drivers/gpu/drm/starfive/Makefile b/drivers/gpu/drm/starfive/Makefile new file mode 100644 index 0000000000000..8ef9e5f469fd1 --- /dev/null +++ b/drivers/gpu/drm/starfive/Makefile @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Copyright (C) 2021 StarFive Technology Co., Ltd. +# +starfive-drm-y := starfive_drm_drv.o \ + starfive_drm_gem.o \ + starfive_drm_crtc.o \ + starfive_drm_encoder.o \ + starfive_drm_plane.o \ + starfive_drm_lcdc.o \ + starfive_drm_vpp.o + +obj-$(CONFIG_DRM_STARFIVE) += starfive-drm.o diff --git a/drivers/gpu/drm/starfive/README.txt b/drivers/gpu/drm/starfive/README.txt new file mode 100644 index 0000000000000..dadec80c98bf5 --- /dev/null +++ b/drivers/gpu/drm/starfive/README.txt @@ -0,0 +1,56 @@ +Display Subsystem:(default FBdev) + +Steps switch to DRM: +1、Disable fbdev,close below config items: +CONFIG_FB_STARFIVE=y +CONFIG_FB_STARFIVE_HDMI_TDA998X=y +CONFIG_FB_STARFIVE_VIDEO=y + +2、open DRM hdmi pipeline,enable items: +CONFIG_DRM_I2C_NXP_TDA998X=y +CONFIG_DRM_I2C_NXP_TDA9950=y +CONFIG_DRM_STARFIVE=y +CONFIG_FRAMEBUFFER_CONSOLE=y + +Precautions:when use DRM hdmi pipeline,please make sure CONFIG_DRM_STARFIVE_MIPI_DSI is disable , + or will cause color abnormal. + +3、open DRM mipi pipeline + +enable items: + CONFIG_PHY_M31_DPHY_RX0=y + CONFIG_DRM_STARFIVE_MIPI_DSI=y + + +change jh7100.dtsi display-encoder as below: + + display-encoder { + compatible = "starfive,display-encoder"; + encoder-type = <6>; //2-TMDS, 3-LVDS, 6-DSI, 8-DPI + status = "okay"; + + ports { + port@0 { + endpoint { + remote-endpoint = <&dsi_out_port>; + }; + }; + + port@1 { + endpoint { + remote-endpoint = <&crtc_0_out>; + }; + }; + }; + }; + +install libdrm: +make buildroot_initramfs-menuconfig +choose: +BR2_PACKAGE_LIBDRM=y +BR2_PACKAGE_LIBDRM_RADEON=y +BR2_PACKAGE_LIBDRM_AMDGPU=y +BR2_PACKAGE_LIBDRM_NOUVEAU=y +BR2_PACKAGE_LIBDRM_ETNAVIV=y +BR2_PACKAGE_LIBDRM_INSTALL_TESTS=y + diff --git a/drivers/gpu/drm/starfive/starfive_drm_crtc.c b/drivers/gpu/drm/starfive/starfive_drm_crtc.c new file mode 100644 index 0000000000000..805e4fb4608cd --- /dev/null +++ b/drivers/gpu/drm/starfive/starfive_drm_crtc.c @@ -0,0 +1,520 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 StarFive Technology Co., Ltd. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "starfive_drm_drv.h" +#include "starfive_drm_crtc.h" +#include "starfive_drm_plane.h" +#include "starfive_drm_lcdc.h" +#include "starfive_drm_vpp.h" +//#include