From 5fa86a4e2bd4e8c95460efc4a4a4a6f772f66238 Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Mon, 2 Sep 2024 10:14:20 +0200 Subject: [PATCH 01/13] mediatek: mt7988a-rfb: align order of SFPs with SDK build MediaTek SDK uses eth2 as additional LAN port and eth1 as WAN. Do the same also in OpenWrt to keep settings aligned with the SDK. Suggested-by: Bc-bocun Chen Signed-off-by: Daniel Golle --- target/linux/mediatek/filogic/base-files/etc/board.d/02_network | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/linux/mediatek/filogic/base-files/etc/board.d/02_network b/target/linux/mediatek/filogic/base-files/etc/board.d/02_network index abe7682dfb..1eecfa81e3 100644 --- a/target/linux/mediatek/filogic/base-files/etc/board.d/02_network +++ b/target/linux/mediatek/filogic/base-files/etc/board.d/02_network @@ -82,7 +82,7 @@ mediatek_setup_interfaces() ucidef_set_interfaces_lan_wan "lan0 lan1 lan2 lan3" eth1 ;; mediatek,mt7988a-rfb) - ucidef_set_interfaces_lan_wan "lan0 lan1 lan2 lan3 eth1" eth2 + ucidef_set_interfaces_lan_wan "lan0 lan1 lan2 lan3 eth2" eth1 ;; mercusys,mr90x-v1) ucidef_set_interfaces_lan_wan "lan0 lan1 lan2" eth1 From cb44f7ce3b93e22767faa3121c1ee7a41622eeb1 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 2 Sep 2024 10:39:06 +0200 Subject: [PATCH 02/13] uboot-mediatek: fix broken patch Add missing --- line Signed-off-by: Felix Fietkau --- .../uboot-mediatek/patches/431-add-xiaomi_redmi-ax6000.patch | 1 + 1 file changed, 1 insertion(+) diff --git a/package/boot/uboot-mediatek/patches/431-add-xiaomi_redmi-ax6000.patch b/package/boot/uboot-mediatek/patches/431-add-xiaomi_redmi-ax6000.patch index a1663bcb62..324d7a9c21 100644 --- a/package/boot/uboot-mediatek/patches/431-add-xiaomi_redmi-ax6000.patch +++ b/package/boot/uboot-mediatek/patches/431-add-xiaomi_redmi-ax6000.patch @@ -1,3 +1,4 @@ +--- /dev/null +++ b/configs/mt7986_xiaomi_redmi-ax6000_defconfig @@ -0,0 +1,104 @@ +CONFIG_ARM=y From 978aa43e5590e82535924521f4e3abbf555a4ed1 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 2 Sep 2024 10:40:05 +0200 Subject: [PATCH 03/13] uboot-mediatek: fix build error on mt7981-rfb and openwrt-one Remove an unnecessary config option that was breaking the build Signed-off-by: Felix Fietkau --- .../107-configs-add-useful-options-to-mt7981-rfb.patch | 10 ++++------ .../patches/314-mt7981-select-rootdisk.patch | 4 ++-- .../uboot-mediatek/patches/453-add-openwrt-one.patch | 6 ++---- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/package/boot/uboot-mediatek/patches/107-configs-add-useful-options-to-mt7981-rfb.patch b/package/boot/uboot-mediatek/patches/107-configs-add-useful-options-to-mt7981-rfb.patch index b33ba4bdee..4f5ef91d35 100644 --- a/package/boot/uboot-mediatek/patches/107-configs-add-useful-options-to-mt7981-rfb.patch +++ b/package/boot/uboot-mediatek/patches/107-configs-add-useful-options-to-mt7981-rfb.patch @@ -87,7 +87,7 @@ CONFIG_CLK=y --- a/configs/mt7981_rfb_defconfig +++ b/configs/mt7981_rfb_defconfig -@@ -11,7 +11,23 @@ CONFIG_DEBUG_UART_BASE=0x11002000 +@@ -11,7 +11,22 @@ CONFIG_DEBUG_UART_BASE=0x11002000 CONFIG_DEBUG_UART_CLOCK=40000000 CONFIG_SYS_LOAD_ADDR=0x46000000 CONFIG_DEBUG_UART=y @@ -106,13 +106,12 @@ +CONFIG_LED_BLINK=y +CONFIG_LED_GPIO=y +CONFIG_SPI_BOOT=y -+CONFIG_NAND_BOOT=y +CONFIG_BOOTSTD_DEFAULTS=y +CONFIG_BOOTSTD_FULL=y CONFIG_DEFAULT_FDT_FILE="mt7981-rfb" CONFIG_SYS_CBSIZE=512 CONFIG_SYS_PBSIZE=1049 -@@ -22,23 +38,74 @@ CONFIG_SYS_PROMPT="MT7981> " +@@ -22,23 +37,74 @@ CONFIG_SYS_PROMPT="MT7981> " # CONFIG_BOOTM_PLAN9 is not set # CONFIG_BOOTM_RTEMS is not set # CONFIG_BOOTM_VXWORKS is not set @@ -278,7 +277,7 @@ CONFIG_CLK=y --- a/configs/mt7981_snfi_nand_rfb_defconfig +++ b/configs/mt7981_snfi_nand_rfb_defconfig -@@ -12,7 +12,23 @@ CONFIG_DEBUG_UART_BASE=0x11002000 +@@ -12,7 +12,22 @@ CONFIG_DEBUG_UART_BASE=0x11002000 CONFIG_DEBUG_UART_CLOCK=40000000 CONFIG_SYS_LOAD_ADDR=0x46000000 CONFIG_DEBUG_UART=y @@ -297,13 +296,12 @@ +CONFIG_LED_BLINK=y +CONFIG_LED_GPIO=y +CONFIG_SPI_BOOT=y -+CONFIG_NAND_BOOT=y +CONFIG_BOOTSTD_DEFAULTS=y +CONFIG_BOOTSTD_FULL=y CONFIG_DEFAULT_FDT_FILE="mt7981-snfi-nand-rfb" CONFIG_LOGLEVEL=7 CONFIG_LOG=y -@@ -22,22 +38,73 @@ CONFIG_SYS_PBSIZE=1049 +@@ -22,22 +37,73 @@ CONFIG_SYS_PBSIZE=1049 # CONFIG_BOOTM_PLAN9 is not set # CONFIG_BOOTM_RTEMS is not set # CONFIG_BOOTM_VXWORKS is not set diff --git a/package/boot/uboot-mediatek/patches/314-mt7981-select-rootdisk.patch b/package/boot/uboot-mediatek/patches/314-mt7981-select-rootdisk.patch index c53e4a8752..05b620a276 100644 --- a/package/boot/uboot-mediatek/patches/314-mt7981-select-rootdisk.patch +++ b/package/boot/uboot-mediatek/patches/314-mt7981-select-rootdisk.patch @@ -81,7 +81,7 @@ +CONFIG_OF_SYSTEM_SETUP=y --- a/configs/mt7981_rfb_defconfig +++ b/configs/mt7981_rfb_defconfig -@@ -135,3 +135,4 @@ CONFIG_DM_SPI=y +@@ -134,3 +134,4 @@ CONFIG_DM_SPI=y CONFIG_MTK_SPIM=y CONFIG_HEXDUMP=y CONFIG_LMB_MAX_REGIONS=64 @@ -95,7 +95,7 @@ +CONFIG_OF_SYSTEM_SETUP=y --- a/configs/mt7981_snfi_nand_rfb_defconfig +++ b/configs/mt7981_snfi_nand_rfb_defconfig -@@ -120,3 +120,4 @@ CONFIG_DM_SERIAL=y +@@ -119,3 +119,4 @@ CONFIG_DM_SERIAL=y CONFIG_MTK_SERIAL=y CONFIG_HEXDUMP=y CONFIG_LMB_MAX_REGIONS=64 diff --git a/package/boot/uboot-mediatek/patches/453-add-openwrt-one.patch b/package/boot/uboot-mediatek/patches/453-add-openwrt-one.patch index b35203bc12..0178c8da6d 100644 --- a/package/boot/uboot-mediatek/patches/453-add-openwrt-one.patch +++ b/package/boot/uboot-mediatek/patches/453-add-openwrt-one.patch @@ -206,7 +206,7 @@ +}; --- /dev/null +++ b/configs/mt7981_openwrt-one-nor_defconfig -@@ -0,0 +1,129 @@ +@@ -0,0 +1,128 @@ +CONFIG_ARM=y +CONFIG_SYS_HAS_NONCACHED_MEMORY=y +CONFIG_POSITION_INDEPENDENT=y @@ -224,7 +224,6 @@ +CONFIG_SYS_LOAD_ADDR=0x46000000 +CONFIG_DEBUG_UART=y +CONFIG_FIT=y -+CONFIG_NAND_BOOT=y +CONFIG_SPI_BOOT=y +CONFIG_AUTOBOOT_MENU_SHOW=y +CONFIG_USE_PREBOOT=y @@ -338,7 +337,7 @@ +CONFIG_LMB_MAX_REGIONS=64 --- /dev/null +++ b/configs/mt7981_openwrt-one-spi-nand_defconfig -@@ -0,0 +1,130 @@ +@@ -0,0 +1,129 @@ +CONFIG_ARM=y +CONFIG_SYS_HAS_NONCACHED_MEMORY=y +CONFIG_POSITION_INDEPENDENT=y @@ -355,7 +354,6 @@ +CONFIG_SYS_LOAD_ADDR=0x46000000 +CONFIG_DEBUG_UART=y +CONFIG_FIT=y -+CONFIG_NAND_BOOT=y +CONFIG_SPI_BOOT=y +CONFIG_AUTOBOOT_MENU_SHOW=y +CONFIG_USE_PREBOOT=y From fd3d79862290197091678e9211ba071647756206 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 2 Sep 2024 10:58:18 +0200 Subject: [PATCH 04/13] u-boot.mk: pass CROSS_COMPILE during config Avoids running into unset architecture specific config symbols Signed-off-by: Felix Fietkau --- include/u-boot.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/u-boot.mk b/include/u-boot.mk index 454880989b..1bd7ed4bd5 100644 --- a/include/u-boot.mk +++ b/include/u-boot.mk @@ -110,10 +110,10 @@ define Build/U-Boot/Target endef define Build/Configure/U-Boot - +$(MAKE) $(PKG_JOBS) -C $(PKG_BUILD_DIR) $(UBOOT_CONFIGURE_VARS) $(UBOOT_CONFIG)_config + +$(MAKE) $(PKG_JOBS) -C $(PKG_BUILD_DIR) CROSS_COMPILE=$(TARGET_CROSS) $(UBOOT_CONFIGURE_VARS) $(UBOOT_CONFIG)_config $(if $(strip $(UBOOT_CUSTOMIZE_CONFIG)), $(PKG_BUILD_DIR)/scripts/config --file $(PKG_BUILD_DIR)/.config $(UBOOT_CUSTOMIZE_CONFIG) - +$(MAKE) $(PKG_JOBS) -C $(PKG_BUILD_DIR) $(UBOOT_CONFIGURE_VARS) oldconfig) + +$(MAKE) $(PKG_JOBS) -C $(PKG_BUILD_DIR) CROSS_COMPILE=$(TARGET_CROSS) $(UBOOT_CONFIGURE_VARS) oldconfig) endef ifndef UBOOT_USE_INTREE_DTC From bc2e5f10d4e8ee173c5d8d13b27263d5f19044b6 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 2 Sep 2024 11:20:13 +0200 Subject: [PATCH 05/13] uboot-mediatek: add missing dependency for mt7988_rfb-spim-nand Signed-off-by: Felix Fietkau --- package/boot/uboot-mediatek/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/boot/uboot-mediatek/Makefile b/package/boot/uboot-mediatek/Makefile index b3b11a0f7c..431ab54c74 100644 --- a/package/boot/uboot-mediatek/Makefile +++ b/package/boot/uboot-mediatek/Makefile @@ -686,7 +686,7 @@ define U-Boot/mt7988_rfb-spim-nand BL2_BOOTDEV:=spim-nand BL2_SOC:=mt7988 BL2_DDRTYPE:=comb - DEPENDS:=+trusted-firmware-a-mt7988-spim-nand-comb + DEPENDS:=+trusted-firmware-a-mt7988-spim-nand-comb +trusted-firmware-a-mt7988-spim-nand-ubi-comb endef define U-Boot/mt7988_rfb-snand From 77037b09f3ce3284c8486b73cfaaceac53241990 Mon Sep 17 00:00:00 2001 From: Jakob Haufe Date: Thu, 22 Aug 2024 23:26:04 +0200 Subject: [PATCH 06/13] octeon: ubnt-edgerouter: fix sysupgrade config backup/restore er is missing from platform_copy_config() and octeon_move_config(), so config is lost on every sysupgrade. Signed-off-by: Jakob Haufe Link: https://github.com/openwrt/openwrt/pull/16238 Signed-off-by: Robert Marko --- target/linux/octeon/base-files/lib/preinit/79_move_config | 3 ++- target/linux/octeon/base-files/lib/upgrade/platform.sh | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/target/linux/octeon/base-files/lib/preinit/79_move_config b/target/linux/octeon/base-files/lib/preinit/79_move_config index 01491500a0..e81627aaf2 100644 --- a/target/linux/octeon/base-files/lib/preinit/79_move_config +++ b/target/linux/octeon/base-files/lib/preinit/79_move_config @@ -22,7 +22,8 @@ octeon_move_config() { itus,shield-router) move_config "/dev/mmcblk1p1" ;; - ubnt,edgerouter-4 | \ + er|\ + ubnt,edgerouter-4|\ ubnt,edgerouter-6p) move_config "/dev/mmcblk0p1" ;; diff --git a/target/linux/octeon/base-files/lib/upgrade/platform.sh b/target/linux/octeon/base-files/lib/upgrade/platform.sh index 14b3eefe7e..0b018890c5 100755 --- a/target/linux/octeon/base-files/lib/upgrade/platform.sh +++ b/target/linux/octeon/base-files/lib/upgrade/platform.sh @@ -34,6 +34,7 @@ platform_copy_config() { itus,shield-router) platform_copy_config_helper /dev/mmcblk1p1 ;; + er|\ ubnt,edgerouter-4|\ ubnt,edgerouter-6p) platform_copy_config_helper /dev/mmcblk0p1 From 64dae1052b41801c8f4fb038b5e63b11e9001b9b Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Thu, 22 Aug 2024 03:25:12 +0100 Subject: [PATCH 07/13] ramips: mt76x8: add support for Yuncore CPE200 Yuncore CPE200 is an outdoor unit with IEEE 802.11ac radio. Hardware: - SoC: MediaTek MT7628DAN (MIPS 580MHz) - Flash: 8 MiB Spansion S25FL064K - RAM: 64 MiB (built-into SoC) - WLAN: 5 GHz (MT7613AE) - Ethernet: 1x 10/100 Mbps WAN, 1x 10/100 LAN (MT7628) - Buttons: 1 Reset button, 2 buttons for display UI (unsupported) - LEDs: 4x Green (Power, LAN, WAN, WiFi) - Display: 4 digit 7-segment display driven by an additional microcontroller (unsupported) - Serial console: unpopulated header, 57600 8n1 (RX only) - Power: 12 VDC, 1 A Installation: The installation can be done via the recovery HTTP server which is built into the bootloader. Hold down the reset button while connecting the device to power and keep holding a bit more than 3 seconds. Connect to http://192.168.0.100/ and upload sysupgrade.bin file. Signed-off-by: Daniel Golle --- .../ramips/dts/mt7628an_yuncore_cpe200.dts | 164 ++++++++++++++++++ target/linux/ramips/image/mt76x8.mk | 8 + .../mt76x8/base-files/etc/board.d/01_leds | 1 + .../mt76x8/base-files/etc/board.d/02_network | 4 + 4 files changed, 177 insertions(+) create mode 100644 target/linux/ramips/dts/mt7628an_yuncore_cpe200.dts diff --git a/target/linux/ramips/dts/mt7628an_yuncore_cpe200.dts b/target/linux/ramips/dts/mt7628an_yuncore_cpe200.dts new file mode 100644 index 0000000000..c8bd39b6ac --- /dev/null +++ b/target/linux/ramips/dts/mt7628an_yuncore_cpe200.dts @@ -0,0 +1,164 @@ +#include "mt7628an.dtsi" + +#include +#include +#include + +/ { + compatible = "yuncore,cpe200", "mediatek,mt7628an-soc"; + + chosen { + bootargs = "console=ttyS0,57600"; + }; + + aliases { + label-mac = ðernet; + led-boot = &led_power; + led-failsafe = &led_power; + led-running = &led_power; + led-upgrade = &led_power; + }; + + keys { + compatible = "gpio-keys"; + + reset { + label = "reset"; + gpios = <&gpio 38 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + + mode { + label = "mode"; + linux,input-type = ; + linux,code = ; + gpios = <&gpio 0 GPIO_ACTIVE_LOW>; + debounce-interval = <60>; + }; + }; + + leds { + compatible = "gpio-leds"; + + led_power: power { + function = LED_FUNCTION_POWER; + color = ; + gpios = <&gpio 11 GPIO_ACTIVE_LOW>; + }; + + lan { + function = LED_FUNCTION_LAN; + color = ; + gpios = <&gpio 43 GPIO_ACTIVE_LOW>; + }; + + wan { + function = LED_FUNCTION_WAN; + color = ; + gpios = <&gpio 39 GPIO_ACTIVE_LOW>; + }; + }; + + watchdog { + compatible = "linux,wdt-gpio"; + gpios = <&gpio 37 GPIO_ACTIVE_HIGH>; + hw_algo = "toggle"; + hw_margin_ms = <20000>; + always-running; + }; +}; + +&spi0 { + status = "okay"; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <10000000>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "u-boot"; + reg = <0x0 0x30000>; + read-only; + }; + + partition@30000 { + label = "u-boot-env"; + reg = <0x30000 0x10000>; + read-only; + }; + + partition@40000 { + label = "factory"; + reg = <0x40000 0x10000>; + read-only; + + nvmem-layout { + compatible = "fixed-layout"; + #address-cells = <1>; + #size-cells = <1>; + + macaddr_factory_4: macaddr@4 { + reg = <0x4 0x6>; + }; + + eeprom_factory_8000: eeprom@8000 { + reg = <0x8000 0x600>; + }; + + macaddr_factory_8004: macaddr@8004 { + reg = <0x8004 0x6>; + }; + }; + }; + + partition@50000 { + compatible = "denx,uimage"; + label = "firmware"; + reg = <0x50000 0x7b0000>; + }; + }; + }; +}; + +ðernet { + nvmem-cells = <&macaddr_factory_4>; + nvmem-cell-names = "mac-address"; +}; + +&esw { + mediatek,portmap = <0x2f>; +}; + +&pcie { + status = "okay"; +}; + +&pcie0 { + mt76@0,0 { + reg = <0x0000 0 0 0 0>; + ieee80211-freq-limit = <5000000 6000000>; + nvmem-cells = <&eeprom_factory_8000>, <&macaddr_factory_8004>; + nvmem-cell-names = "eeprom", "mac-address"; + + led { + led-sources = <0>; + led-active-low; + }; + }; +}; + +&state_default { + gpio { + groups = "pwm0", "i2c", "refclk", "wdt", "i2s", "spi cs1", + "spis", "gpio", "p0led_an", "p1led_an", "p2led_an", + "p3led_an", "p4led_an", "wled_kn", "p0led_kn", + "p1led_kn", "p2led_kn", "p3led_kn", "p4led_kn", "wled_an"; + function = "gpio"; + }; +}; diff --git a/target/linux/ramips/image/mt76x8.mk b/target/linux/ramips/image/mt76x8.mk index 1eae3d3dee..8ad840ddfe 100644 --- a/target/linux/ramips/image/mt76x8.mk +++ b/target/linux/ramips/image/mt76x8.mk @@ -1136,6 +1136,14 @@ define Device/xiaomi_mi-ra75 endef TARGET_DEVICES += xiaomi_mi-ra75 +define Device/yuncore_cpe200 + IMAGE_SIZE := 7872k + DEVICE_VENDOR := Yuncore + DEVICE_MODEL := CPE200 + DEVICE_PACKAGES := -kmod-mt7603 kmod-mt7615e kmod-mt7663-firmware-ap kmod-mt7663-firmware-sta +endef +TARGET_DEVICES += yuncore_cpe200 + define Device/yuncore_m300 IMAGE_SIZE := 7872k DEVICE_VENDOR := Yuncore diff --git a/target/linux/ramips/mt76x8/base-files/etc/board.d/01_leds b/target/linux/ramips/mt76x8/base-files/etc/board.d/01_leds index 5a93761780..5ea668de01 100644 --- a/target/linux/ramips/mt76x8/base-files/etc/board.d/01_leds +++ b/target/linux/ramips/mt76x8/base-files/etc/board.d/01_leds @@ -19,6 +19,7 @@ asus,rt-n12-vp-b1|\ netgear,r6020|\ netgear,r6080|\ netgear,r6120|\ +yuncore,cpe200|\ yuncore,m300) ucidef_set_led_switch "lan" "lan" "green:lan" "switch0" "0xf" ucidef_set_led_switch "wan" "wan" "green:wan" "switch0" "0x10" diff --git a/target/linux/ramips/mt76x8/base-files/etc/board.d/02_network b/target/linux/ramips/mt76x8/base-files/etc/board.d/02_network index 2989d36e9b..cb0983b9dc 100644 --- a/target/linux/ramips/mt76x8/base-files/etc/board.d/02_network +++ b/target/linux/ramips/mt76x8/base-files/etc/board.d/02_network @@ -204,6 +204,10 @@ ramips_setup_interfaces() ucidef_add_switch "switch0" \ "0:lan:2" "2:lan:1" "4:wan" "6@eth0" ;; + yuncore,cpe200) + ucidef_add_switch "switch0" \ + "0:lan" "4:wan" "6@eth0" + ;; zbtlink,zbt-we1226) ucidef_add_switch "switch0" \ "0:lan:2" "1:lan:1" "4:wan" "6@eth0" From 9bc782dfd8b09a96812771124eea56cbd230d9ad Mon Sep 17 00:00:00 2001 From: Christian Svensson Date: Sun, 11 Aug 2024 13:27:49 +0200 Subject: [PATCH 08/13] octeon: n821: sysupgrade for Cisco vEdge 1000 This commit adds the logic required to install OpenWrt on a Cisco/Viptela vEdge 1000 while nicely integrating with the already present bootloader and making it possible to roll back to stock OS. The vEdge 1000 has a built-in SSD with a ext2 partition from which the stock u-boot boots the Linux kernel from. In order for Linux not to be confused about which partition the root file system is on, there are some tricks needed to ensure the partition is identifiable at boot time. For this we use blkid as part of the scripts, and sfdisk as part of the manual installation process documented on the wiki. Thus, those packages have been added as a platform dependency. Finally, the u-boot environment is updated which requires the use of uboot-envtools. Tested on a Cisco vEdge 1000. Signed-off-by: Christian Svensson Link: https://github.com/openwrt/openwrt/pull/16140 Signed-off-by: Robert Marko --- .../base-files/lib/preinit/79_move_config | 25 ++++++-- .../octeon/base-files/lib/upgrade/platform.sh | 61 ++++++++++++++++--- target/linux/octeon/image/Makefile | 5 +- 3 files changed, 77 insertions(+), 14 deletions(-) diff --git a/target/linux/octeon/base-files/lib/preinit/79_move_config b/target/linux/octeon/base-files/lib/preinit/79_move_config index e81627aaf2..db99e02ce8 100644 --- a/target/linux/octeon/base-files/lib/preinit/79_move_config +++ b/target/linux/octeon/base-files/lib/preinit/79_move_config @@ -4,29 +4,46 @@ move_config() { . /lib/upgrade/common.sh local device="$1" + local fstype="$2" [ -n "$device" ] && [ -b "$device" ] && { - mount -t vfat "$device" /mnt + mount -t "${fstype}" "$device" /mnt [ -f "/mnt/$BACKUP_FILE" ] && mv -f "/mnt/$BACKUP_FILE" / umount /mnt } } +octeon_get_n821_disk() { + local partnum=$1 + local MAJOR MINOR DEVNAME DEVTYPE + while read line; do + export -n "${line}" + done < $(find /sys/bus/platform/devices/16f0000000000.ehci/ -path \*block/sd[a-z]/uevent) + echo "/dev/${DEVNAME}${partnum}" +} + octeon_move_config() { . /lib/functions.sh case "$(board_name)" in erlite|\ ubnt,usg) - move_config "/dev/sda1" + move_config "/dev/sda1" "vfat" ;; itus,shield-router) - move_config "/dev/mmcblk1p1" + move_config "/dev/mmcblk1p1" "vfat" ;; er|\ ubnt,edgerouter-4|\ ubnt,edgerouter-6p) - move_config "/dev/mmcblk0p1" + move_config "/dev/mmcblk0p1" "vfat" ;; + cisco,vedge1000) + # Copy from the internal USB disk's first partition. + # It is resolved from the device path to not be dependent on which + # /dev/sd? path it is at, nor which UUID it happens to have. + move_config "$(octeon_get_n821_disk 1)" "ext2" + ;; + esac } diff --git a/target/linux/octeon/base-files/lib/upgrade/platform.sh b/target/linux/octeon/base-files/lib/upgrade/platform.sh index 0b018890c5..b8a856712b 100755 --- a/target/linux/octeon/base-files/lib/upgrade/platform.sh +++ b/target/linux/octeon/base-files/lib/upgrade/platform.sh @@ -2,11 +2,21 @@ # Copyright (C) 2021 OpenWrt.org # +if [ -x /usr/sbin/blkid ]; then + RAMFS_COPY_BIN="/usr/sbin/blkid" +fi + platform_get_rootfs() { local rootfsdev + local rootpartuuid if read cmdline < /proc/cmdline; then case "$cmdline" in + *root=PARTUUID=*) + rootpartuuid="${cmdline##*root=PARTUUID=}" + rootpartuuid="${rootpartuuid%% *}" + rootfsdev="$(blkid -o device -t PARTUUID="${rootpartuuid}")" + ;; *root=*) rootfsdev="${cmdline##*root=}" rootfsdev="${rootfsdev%% *}" @@ -17,10 +27,20 @@ platform_get_rootfs() { fi } +platform_get_n821_disk() { + local partnum=$1 + local DEVNAME + while read line; do + export -n "${line}" + done < $(find /sys/bus/platform/devices/16f0000000000.ehci/ -path \*block/sd[a-z]/uevent) + echo "/dev/${DEVNAME}${partnum}" +} + platform_copy_config_helper() { local device=$1 + local fstype=$2 - mount -t vfat "$device" /mnt + mount -t "${fstype}" "$device" /mnt cp -af "$UPGRADE_BACKUP" "/mnt/$BACKUP_FILE" umount /mnt } @@ -29,15 +49,18 @@ platform_copy_config() { case "$(board_name)" in erlite|\ ubnt,usg) - platform_copy_config_helper /dev/sda1 + platform_copy_config_helper /dev/sda1 vfat ;; itus,shield-router) - platform_copy_config_helper /dev/mmcblk1p1 + platform_copy_config_helper /dev/mmcblk1p1 vfat ;; er|\ ubnt,edgerouter-4|\ ubnt,edgerouter-6p) - platform_copy_config_helper /dev/mmcblk0p1 + platform_copy_config_helper /dev/mmcblk0p1 vfat + ;; + cisco,vedge1000) + platform_copy_config_helper "$(platform_get_n821_disk 1)" ext2 ;; esac } @@ -61,14 +84,26 @@ platform_do_flash() { echo "flashing Itus Kernel to /boot/$kernel (/dev/mmblk1p1)" tar -Oxf $tar_file "$board_dir/kernel" > /boot/$kernel else - mount -t vfat /dev/$kernel /boot + if [ "${board}" = "cisco,vedge1000" ]; then + local rootpartuuid + rootpartuuid="$(/usr/sbin/blkid -o value -s PARTUUID "${rootfs}")" + if [ -n "${rootpartuuid}" ]; then + echo "setting root partition to PARTUUID=${rootpartuuid}" + fw_setenv bootcmd 'usb start; ext2load usb 0:1 $loadaddr vmlinux.64; bootoctlinux $loadaddr coremask=f endbootargs rootfstype=squashfs rootwait root=PARTUUID='"${rootpartuuid}" + else + echo "WARNING: unable to figure out root partition UUID, leaving bootcmd unchanged" + fi + mount -t ext2 "${kernel}" /boot + else + mount -t vfat "${kernel}" /boot + fi [ -f /boot/vmlinux.64 -a ! -L /boot/vmlinux.64 ] && { mv /boot/vmlinux.64 /boot/vmlinux.64.previous mv /boot/vmlinux.64.md5 /boot/vmlinux.64.md5.previous } - echo "flashing kernel to /dev/$kernel" + echo "flashing kernel to $(awk '/\/boot/ {print $1}' /proc/mounts)" tar xf $tar_file $board_dir/kernel -O > /boot/vmlinux.64 md5sum /boot/vmlinux.64 | cut -f1 -d " " > /boot/vmlinux.64.md5 fi @@ -86,20 +121,27 @@ platform_do_upgrade() { local rootfs="$(platform_get_rootfs)" local kernel= + if [ ! -b "${rootfs}" ] && [ "${board}" = "cisco,vedge1000" ]; then + # Default to the built-in USB disk for N821 + rootfs="$(platform_get_n821_disk 2)" + fi [ -b "${rootfs}" ] || return 1 case "$board" in er | \ ubnt,edgerouter-4 | \ ubnt,edgerouter-6p) - kernel=mmcblk0p1 + kernel=/dev/mmcblk0p1 ;; erlite|\ ubnt,usg) - kernel=sda1 + kernel=/dev/sda1 ;; itus,shield-router) kernel=ItusrouterImage ;; + cisco,vedge1000) + kernel="$(platform_get_n821_disk 1)" + ;; *) return 1 esac @@ -123,7 +165,8 @@ platform_check_image() { itus,shield-router | \ ubnt,edgerouter-4 | \ ubnt,edgerouter-6p | \ - ubnt,usg) + ubnt,usg | \ + cisco,vedge1000) local kernel_length=$(tar xf $tar_file $board_dir/kernel -O | wc -c 2> /dev/null) local rootfs_length=$(tar xf $tar_file $board_dir/root -O | wc -c 2> /dev/null) [ "$kernel_length" = 0 -o "$rootfs_length" = 0 ] && { diff --git a/target/linux/octeon/image/Makefile b/target/linux/octeon/image/Makefile index 536ec5ff7c..f00e87c520 100644 --- a/target/linux/octeon/image/Makefile +++ b/target/linux/octeon/image/Makefile @@ -100,13 +100,16 @@ define Device/cisco_vedge1000 DEVICE_MODEL := vEdge 1000 BOARD_NAME := cisco,vedge1000 DEVICE_PACKAGES += \ + blkid \ kmod-hwmon-jc42 \ kmod-hwmon-max6697 \ kmod-of-mdio \ kmod-rtc-ds1307 \ kmod-usb-dwc3 \ kmod-usb-storage-uas \ - kmod-usb3 + kmod-usb3 \ + sfdisk \ + uboot-envtools KERNEL := kernel-bin | append-dtb-elf KERNEL_DEPENDS := $$(wildcard $(DTS_DIR)/$(DEVICE_DTS).dts) DEVICE_DTS := cn6130_cisco_vedge1000 From 0ba0493bc86909368e455f8a2b1cf92151b02950 Mon Sep 17 00:00:00 2001 From: Zoltan HERPAI Date: Sun, 1 Sep 2024 14:05:25 +0000 Subject: [PATCH 09/13] kernel/starfive: Create kernel files for v6.6 (from v6.1) This is an automatically generated commit. When doing `git bisect`, consider `git bisect --skip`. Signed-off-by: Zoltan HERPAI --- target/linux/starfive/{config-6.1 => config-6.6} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename target/linux/starfive/{config-6.1 => config-6.6} (100%) diff --git a/target/linux/starfive/config-6.1 b/target/linux/starfive/config-6.6 similarity index 100% rename from target/linux/starfive/config-6.1 rename to target/linux/starfive/config-6.6 From b27260d0d8872e09f3faa28c423c45a2a50735c2 Mon Sep 17 00:00:00 2001 From: Zoltan HERPAI Date: Sun, 1 Sep 2024 14:05:25 +0000 Subject: [PATCH 10/13] kernel/starfive: Restore kernel files for v6.1 This is an automatically generated commit which aids following Kernel patch history, as git will see the move and copy as a rename thus defeating the purpose. For the original discussion see: https://lists.openwrt.org/pipermail/openwrt-devel/2023-October/041673.html Signed-off-by: Zoltan HERPAI --- target/linux/starfive/config-6.1 | 555 +++++++++++++++++++++++++++++++ 1 file changed, 555 insertions(+) create mode 100644 target/linux/starfive/config-6.1 diff --git a/target/linux/starfive/config-6.1 b/target/linux/starfive/config-6.1 new file mode 100644 index 0000000000..6da229ddb5 --- /dev/null +++ b/target/linux/starfive/config-6.1 @@ -0,0 +1,555 @@ +CONFIG_64BIT=y +CONFIG_AMBA_PL08X=y +CONFIG_ARCH_CLOCKSOURCE_INIT=y +CONFIG_ARCH_DMA_ADDR_T_64BIT=y +CONFIG_ARCH_HIBERNATION_POSSIBLE=y +CONFIG_ARCH_MMAP_RND_BITS=18 +CONFIG_ARCH_MMAP_RND_BITS_MAX=24 +CONFIG_ARCH_MMAP_RND_BITS_MIN=18 +CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX=17 +CONFIG_ARCH_OPTIONAL_KERNEL_RWX=y +CONFIG_ARCH_OPTIONAL_KERNEL_RWX_DEFAULT=y +CONFIG_ARCH_RV64I=y +CONFIG_ARCH_SELECT_MEMORY_MODEL=y +CONFIG_ARCH_SIFIVE=y +CONFIG_ARCH_SPARSEMEM_ENABLE=y +CONFIG_ARCH_STACKWALK=y +CONFIG_ARCH_STARFIVE=y +CONFIG_ARCH_WANTS_THP_SWAP=y +CONFIG_ARM_AMBA=y +# CONFIG_ARM_MHU_V2 is not set +CONFIG_ASN1=y +CONFIG_ASSOCIATIVE_ARRAY=y +CONFIG_AUXILIARY_BUS=y +CONFIG_CC_HAVE_STACKPROTECTOR_TLS=y +CONFIG_CC_IMPLICIT_FALLTHROUGH="-Wimplicit-fallthrough=5" +CONFIG_CC_NO_ARRAY_BOUNDS=y +CONFIG_CHECKPOINT_RESTORE=y +CONFIG_CLKSRC_MMIO=y +CONFIG_CLK_ANALOGBITS_WRPLL_CLN28HPC=y +CONFIG_CLK_SIFIVE=y +CONFIG_CLK_SIFIVE_PRCI=y +CONFIG_CLK_STARFIVE_JH7100=y +CONFIG_CLK_STARFIVE_JH7100_AUDIO=y +CONFIG_CLK_STARFIVE_JH7110_AON=y +CONFIG_CLK_STARFIVE_JH7110_ISP=y +CONFIG_CLK_STARFIVE_JH7110_PLL=y +CONFIG_CLK_STARFIVE_JH7110_STG=y +CONFIG_CLK_STARFIVE_JH7110_SYS=y +CONFIG_CLK_STARFIVE_JH7110_VOUT=y +CONFIG_CLK_STARFIVE_JH71X0=y +CONFIG_CLONE_BACKWARDS=y +CONFIG_CLZ_TAB=y +CONFIG_CMODEL_MEDANY=y +# CONFIG_CMODEL_MEDLOW is not set +CONFIG_COMMON_CLK=y +CONFIG_COMPACT_UNEVICTABLE_DEFAULT=1 +# CONFIG_COMPAT_32BIT_TIME is not set +CONFIG_CONFIGFS_FS=y +CONFIG_CONTEXT_TRACKING=y +CONFIG_CONTEXT_TRACKING_IDLE=y +CONFIG_CONTIG_ALLOC=y +CONFIG_CPUFREQ_DT=y +CONFIG_CPUFREQ_DT_PLATDEV=y +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y +CONFIG_CPU_FREQ_GOV_ATTR_SET=y +CONFIG_CPU_FREQ_GOV_COMMON=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_IDLE=y +CONFIG_CPU_IDLE_GOV_MENU=y +CONFIG_CPU_IDLE_MULTIPLE_DRIVERS=y +CONFIG_CPU_PM=y +CONFIG_CPU_RMAP=y +CONFIG_CRASH_CORE=y +CONFIG_CRC16=y +CONFIG_CRC7=y +CONFIG_CRC_ITU_T=y +CONFIG_CRYPTO_BLAKE2B=y +CONFIG_CRYPTO_CMAC=y +CONFIG_CRYPTO_CRC32C=y +CONFIG_CRYPTO_DEV_JH7110=y +CONFIG_CRYPTO_DRBG=y +CONFIG_CRYPTO_DRBG_HMAC=y +CONFIG_CRYPTO_DRBG_MENU=y +CONFIG_CRYPTO_ECB=y +CONFIG_CRYPTO_ECC=y +CONFIG_CRYPTO_ECDH=y +CONFIG_CRYPTO_ENGINE=y +CONFIG_CRYPTO_HASH_INFO=y +CONFIG_CRYPTO_HMAC=y +CONFIG_CRYPTO_HW=y +CONFIG_CRYPTO_JITTERENTROPY=y +CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y +CONFIG_CRYPTO_LIB_POLY1305_RSIZE=1 +CONFIG_CRYPTO_LIB_SHA1=y +CONFIG_CRYPTO_LIB_SHA256=y +CONFIG_CRYPTO_LIB_UTILS=y +CONFIG_CRYPTO_RNG=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_RNG_DEFAULT=y +CONFIG_CRYPTO_RSA=y +CONFIG_CRYPTO_SHA256=y +CONFIG_CRYPTO_SHA512=y +CONFIG_CRYPTO_SM3=y +CONFIG_CRYPTO_SM3_GENERIC=y +CONFIG_CRYPTO_USER=y +CONFIG_CRYPTO_USER_API=y +CONFIG_CRYPTO_USER_API_AEAD=y +CONFIG_CRYPTO_USER_API_HASH=y +CONFIG_CRYPTO_USER_API_RNG=y +CONFIG_CRYPTO_USER_API_SKCIPHER=y +CONFIG_CRYPTO_XXHASH=y +CONFIG_CRYPTO_ZSTD=y +CONFIG_DEBUG_ATOMIC_SLEEP=y +CONFIG_DEBUG_GPIO=y +CONFIG_DEBUG_INFO=y +CONFIG_DEBUG_PINCTRL=y +CONFIG_DEBUG_RODATA_TEST=y +CONFIG_DEBUG_RT_MUTEXES=y +CONFIG_DEBUG_RWSEMS=y +CONFIG_DEBUG_SECTION_MISMATCH=y +CONFIG_DEBUG_SG=y +CONFIG_DEBUG_SPINLOCK=y +CONFIG_DEBUG_TIMEKEEPING=y +CONFIG_DEBUG_WX=y +CONFIG_DECOMPRESS_GZIP=y +# CONFIG_DEVFREQ_GOV_PASSIVE is not set +# CONFIG_DEVFREQ_GOV_PERFORMANCE is not set +# CONFIG_DEVFREQ_GOV_POWERSAVE is not set +# CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND is not set +# CONFIG_DEVFREQ_GOV_USERSPACE is not set +# CONFIG_DEVFREQ_THERMAL is not set +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +# CONFIG_DEVTMPFS_SAFE is not set +CONFIG_DMADEVICES=y +CONFIG_DMADEVICES_DEBUG=y +CONFIG_DMADEVICES_VDEBUG=y +CONFIG_DMA_ENGINE=y +CONFIG_DMA_OF=y +CONFIG_DMA_SHARED_BUFFER=y +CONFIG_DMA_VIRTUAL_CHANNELS=y +# CONFIG_DPM_WATCHDOG is not set +CONFIG_DTC=y +CONFIG_DT_IDLE_GENPD=y +CONFIG_DT_IDLE_STATES=y +CONFIG_DWMAC_DWC_QOS_ETH=y +# CONFIG_DWMAC_GENERIC is not set +CONFIG_DWMAC_STARFIVE=y +CONFIG_DW_AXI_DMAC=y +CONFIG_EDAC_SUPPORT=y +CONFIG_EEPROM_AT24=y +CONFIG_EFI=y +CONFIG_EFIVAR_FS=y +# CONFIG_EFI_BOOTLOADER_CONTROL is not set +# CONFIG_EFI_CAPSULE_LOADER is not set +# CONFIG_EFI_COCO_SECRET is not set +# CONFIG_EFI_DISABLE_PCI_DMA is not set +CONFIG_EFI_DISABLE_RUNTIME=y +CONFIG_EFI_EARLYCON=y +CONFIG_EFI_ESRT=y +CONFIG_EFI_GENERIC_STUB=y +CONFIG_EFI_PARAMS_FROM_FDT=y +CONFIG_EFI_RUNTIME_WRAPPERS=y +CONFIG_EFI_STUB=y +# CONFIG_EFI_TEST is not set +# CONFIG_EFI_ZBOOT is not set +CONFIG_ERRATA_SIFIVE=y +CONFIG_ERRATA_SIFIVE_CIP_1200=y +CONFIG_ERRATA_SIFIVE_CIP_453=y +# CONFIG_ERRATA_THEAD is not set +CONFIG_EXCLUSIVE_SYSTEM_RAM=y +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_EXTCON=y +CONFIG_FAILOVER=y +CONFIG_FANOTIFY=y +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-15" +CONFIG_FAT_DEFAULT_UTF8=y +CONFIG_FAT_FS=y +CONFIG_FIXED_PHY=y +CONFIG_FIX_EARLYCON_MEM=y +CONFIG_FONT_8x16=y +CONFIG_FONT_AUTOSELECT=y +CONFIG_FONT_SUPPORT=y +CONFIG_FPU=y +CONFIG_FS_IOMAP=y +CONFIG_FS_MBCACHE=y +CONFIG_FS_POSIX_ACL=y +CONFIG_FWNODE_MDIO=y +CONFIG_FW_LOADER_PAGED_BUF=y +CONFIG_FW_LOADER_SYSFS=y +CONFIG_GCC11_NO_ARRAY_BOUNDS=y +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_GENERIC_ARCH_TOPOLOGY=y +CONFIG_GENERIC_BUG=y +CONFIG_GENERIC_BUG_RELATIVE_POINTERS=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y +CONFIG_GENERIC_CSUM=y +CONFIG_GENERIC_EARLY_IOREMAP=y +CONFIG_GENERIC_GETTIMEOFDAY=y +CONFIG_GENERIC_IDLE_POLL_SETUP=y +CONFIG_GENERIC_IOREMAP=y +CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK=y +CONFIG_GENERIC_IRQ_MIGRATION=y +CONFIG_GENERIC_IRQ_MULTI_HANDLER=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_GENERIC_IRQ_SHOW_LEVEL=y +CONFIG_GENERIC_LIB_DEVMEM_IS_ALLOWED=y +CONFIG_GENERIC_MSI_IRQ=y +CONFIG_GENERIC_MSI_IRQ_DOMAIN=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_PHY=y +CONFIG_GENERIC_PHY_MIPI_DPHY=y +CONFIG_GENERIC_PINCONF=y +CONFIG_GENERIC_PINCTRL_GROUPS=y +CONFIG_GENERIC_PINMUX_FUNCTIONS=y +CONFIG_GENERIC_SCHED_CLOCK=y +CONFIG_GENERIC_SMP_IDLE_THREAD=y +CONFIG_GENERIC_STRNCPY_FROM_USER=y +CONFIG_GENERIC_STRNLEN_USER=y +CONFIG_GENERIC_TIME_VSYSCALL=y +CONFIG_GLOB=y +CONFIG_GPIOLIB_FASTPATH_LIMIT=128 +CONFIG_GPIOLIB_IRQCHIP=y +CONFIG_GPIO_CDEV=y +CONFIG_GPIO_TPS65086=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_HAS_DMA=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT_MAP=y +CONFIG_HOTPLUG_CPU=y +CONFIG_HUGETLBFS=y +CONFIG_HUGETLB_PAGE=y +CONFIG_HVC_DRIVER=y +CONFIG_HVC_RISCV_SBI=y +CONFIG_HWMON=y +CONFIG_HW_RANDOM=y +CONFIG_HW_RANDOM_JH7110=y +CONFIG_HW_RANDOM_STARFIVE_VIC=y +CONFIG_I2C=y +CONFIG_I2C_ALGOBIT=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_DESIGNWARE_CORE=y +CONFIG_I2C_DESIGNWARE_PLATFORM=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_IRQCHIP=y +CONFIG_IRQ_DOMAIN=y +CONFIG_IRQ_DOMAIN_HIERARCHY=y +CONFIG_IRQ_FORCED_THREADING=y +CONFIG_IRQ_WORK=y +CONFIG_JBD2=y +CONFIG_JH71XX_PMU=y +CONFIG_JUMP_LABEL=y +CONFIG_LIBFDT=y +CONFIG_LOCKUP_DETECTOR=y +CONFIG_LOCK_DEBUGGING_SUPPORT=y +CONFIG_LOCK_SPIN_ON_OWNER=y +CONFIG_LSM="" +CONFIG_MARVELL_PHY=y +CONFIG_MDIO_BUS=y +CONFIG_MDIO_DEVICE=y +CONFIG_MDIO_DEVRES=y +CONFIG_MEMFD_CREATE=y +CONFIG_MEMORY_ISOLATION=y +CONFIG_MEMTEST=y +CONFIG_MFD_AXP20X=y +CONFIG_MFD_AXP20X_I2C=y +CONFIG_MFD_CORE=y +CONFIG_MFD_SYSCON=y +CONFIG_MFD_TPS65086=y +CONFIG_MICREL_PHY=y +CONFIG_MICROCHIP_PHY=y +CONFIG_MIGRATION=y +CONFIG_MMC=y +CONFIG_MMC_BLOCK=y +CONFIG_MMC_DEBUG=y +CONFIG_MMC_DW=y +# CONFIG_MMC_DW_BLUEFIELD is not set +# CONFIG_MMC_DW_EXYNOS is not set +# CONFIG_MMC_DW_HI3798CV200 is not set +# CONFIG_MMC_DW_K3 is not set +# CONFIG_MMC_DW_PCI is not set +CONFIG_MMC_DW_PLTFM=y +CONFIG_MMC_DW_STARFIVE=y +CONFIG_MMIOWB=y +CONFIG_MODULES_TREE_LOOKUP=y +CONFIG_MODULES_USE_ELF_RELA=y +CONFIG_MODULE_SECTIONS=y +CONFIG_MOTORCOMM_PHY=y +CONFIG_MPILIB=y +CONFIG_MTD_SPI_NOR=y +CONFIG_MUTEX_SPIN_ON_OWNER=y +CONFIG_NAMESPACES=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_NET_FAILOVER=y +CONFIG_NET_FLOW_LIMIT=y +CONFIG_NET_NS=y +CONFIG_NET_SELFTESTS=y +CONFIG_NLS=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_DEFAULT="iso8859-15" +CONFIG_NLS_ISO8859_1=y +CONFIG_NLS_ISO8859_15=y +CONFIG_NO_HZ_COMMON=y +CONFIG_NO_HZ_IDLE=y +# CONFIG_NONPORTABLE is not set +CONFIG_NR_CPUS=8 +CONFIG_NVMEM=y +CONFIG_NVMEM_SYSFS=y +CONFIG_NVME_CORE=y +CONFIG_NVME_HWMON=y +# CONFIG_NVME_MULTIPATH is not set +CONFIG_OF=y +CONFIG_OF_ADDRESS=y +CONFIG_OF_DMA_DEFAULT_COHERENT=y +CONFIG_OF_DYNAMIC=y +CONFIG_OF_EARLY_FLATTREE=y +CONFIG_OF_FLATTREE=y +CONFIG_OF_GPIO=y +CONFIG_OF_IRQ=y +CONFIG_OF_KOBJ=y +CONFIG_OF_MDIO=y +CONFIG_OF_OVERLAY=y +CONFIG_OF_RESOLVE=y +CONFIG_OID_REGISTRY=y +CONFIG_OVERLAY_FS_INDEX=y +CONFIG_OVERLAY_FS_METACOPY=y +CONFIG_OVERLAY_FS_REDIRECT_DIR=y +CONFIG_PADATA=y +CONFIG_PAGE_EXTENSION=y +CONFIG_PAGE_OFFSET=0xff60000000000000 +CONFIG_PAGE_POOL=y +CONFIG_PAGE_REPORTING=y +CONFIG_PAGE_SIZE_LESS_THAN_256KB=y +CONFIG_PAGE_SIZE_LESS_THAN_64KB=y +CONFIG_PCI=y +CONFIG_PCIE_CADENCE=y +CONFIG_PCIE_CADENCE_HOST=y +CONFIG_PCIE_CADENCE_PLAT=y +CONFIG_PCIE_CADENCE_PLAT_HOST=y +# CONFIG_PCIE_FU740 is not set +# CONFIG_PCIE_STARFIVE is not set +CONFIG_PCI_DOMAINS=y +CONFIG_PCI_DOMAINS_GENERIC=y +CONFIG_PCI_MSI=y +CONFIG_PCI_MSI_IRQ_DOMAIN=y +CONFIG_PCS_XPCS=y +CONFIG_PERF_EVENTS=y +CONFIG_PGTABLE_LEVELS=5 +CONFIG_PHYLIB=y +CONFIG_PHYLINK=y +CONFIG_PHYS_ADDR_T_64BIT=y +CONFIG_PHY_STARFIVE_DPHY_RX=y +CONFIG_PHY_STARFIVE_JH7110_PCIE=y +CONFIG_PHY_STARFIVE_JH7110_USB=y +CONFIG_PINCTRL=y +CONFIG_PINCTRL_STARFIVE_JH7100=y +CONFIG_PINCTRL_STARFIVE_JH7110=y +CONFIG_PINCTRL_STARFIVE_JH7110_AON=y +CONFIG_PINCTRL_STARFIVE_JH7110_SYS=y +CONFIG_PM=y +CONFIG_PM_ADVANCED_DEBUG=y +CONFIG_PM_CLK=y +CONFIG_PM_DEBUG=y +CONFIG_PM_DEVFREQ=y +# CONFIG_PM_DEVFREQ_EVENT is not set +CONFIG_PM_GENERIC_DOMAINS=y +CONFIG_PM_GENERIC_DOMAINS_OF=y +CONFIG_PM_OPP=y +CONFIG_PORTABLE=y +CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y +CONFIG_POSIX_MQUEUE=y +CONFIG_POSIX_MQUEUE_SYSCTL=y +CONFIG_POWER_RESET=y +CONFIG_POWER_RESET_GPIO_RESTART=y +CONFIG_POWER_RESET_SYSCON=y +CONFIG_POWER_RESET_SYSCON_POWEROFF=y +# CONFIG_POWER_RESET_TPS65086 is not set +CONFIG_PPS=y +CONFIG_PREEMPT_COUNT=y +CONFIG_PREEMPT_NONE_BUILD=y +CONFIG_PRINTK_TIME=y +CONFIG_PROC_CHILDREN=y +CONFIG_PROC_KCORE=y +CONFIG_PTDUMP_CORE=y +CONFIG_PTP_1588_CLOCK=y +CONFIG_PTP_1588_CLOCK_OPTIONAL=y +CONFIG_PWM=y +# CONFIG_PWM_SIFIVE is not set +CONFIG_PWM_SIFIVE_PTC=y +CONFIG_PWM_STARFIVE_PTC=y +CONFIG_PWM_SYSFS=y +CONFIG_QUEUED_RWLOCKS=y +CONFIG_RANDSTRUCT_NONE=y +CONFIG_RATIONAL=y +CONFIG_RCU_EQS_DEBUG=y +CONFIG_RD_GZIP=y +CONFIG_REALTEK_PHY=y +CONFIG_REGMAP=y +CONFIG_REGMAP_I2C=y +CONFIG_REGMAP_IRQ=y +CONFIG_REGMAP_MMIO=y +CONFIG_REGULATOR=y +CONFIG_REGULATOR_AXP20X=y +CONFIG_REGULATOR_TPS65086=y +# CONFIG_RESET_ATTACK_MITIGATION is not set +CONFIG_RESET_CONTROLLER=y +CONFIG_RESET_SIMPLE=y +CONFIG_RESET_STARFIVE_JH7100=y +CONFIG_RESET_STARFIVE_JH7100_AUDIO=y +CONFIG_RESET_STARFIVE_JH7110=y +CONFIG_RESET_STARFIVE_JH71X0=y +CONFIG_RFS_ACCEL=y +CONFIG_RISCV=y +CONFIG_RISCV_ALTERNATIVE=y +CONFIG_RISCV_BOOT_SPINWAIT=y +CONFIG_RISCV_DMA_NONCOHERENT=y +CONFIG_RISCV_INTC=y +CONFIG_RISCV_ISA_C=y +# CONFIG_RISCV_ISA_SVPBMT is not set +CONFIG_RISCV_ISA_ZICBOM=y +CONFIG_RISCV_PMU=y +CONFIG_RISCV_PMU_LEGACY=y +CONFIG_RISCV_PMU_SBI=y +CONFIG_RISCV_SBI=y +CONFIG_RISCV_SBI_CPUIDLE=y +CONFIG_RISCV_SBI_V01=y +CONFIG_RISCV_TIMER=y +CONFIG_RPMSG=y +CONFIG_RPMSG_CHAR=y +# CONFIG_RPMSG_CTRL is not set +CONFIG_RPMSG_NS=y +# CONFIG_RPMSG_TTY is not set +CONFIG_RPMSG_VIRTIO=y +CONFIG_RPS=y +CONFIG_RTC_CLASS=y +# CONFIG_RTC_DRV_EFI is not set +CONFIG_RTC_DRV_GOLDFISH=y +CONFIG_RTC_DRV_HYM8563=y +CONFIG_RTC_I2C_AND_SPI=y +CONFIG_RWSEM_SPIN_ON_OWNER=y +CONFIG_SCSI=y +CONFIG_SCSI_COMMON=y +CONFIG_SCSI_VIRTIO=y +CONFIG_SENSORS_SFCTEMP=y +# CONFIG_SERIAL_8250_16550A_VARIANTS is not set +CONFIG_SERIAL_8250_DW=y +CONFIG_SERIAL_8250_DWLIB=y +CONFIG_SERIAL_8250_EXTENDED=y +CONFIG_SERIAL_8250_MANY_PORTS=y +CONFIG_SERIAL_8250_NR_UARTS=6 +CONFIG_SERIAL_8250_RUNTIME_UARTS=6 +# CONFIG_SERIAL_8250_SHARE_IRQ is not set +CONFIG_SERIAL_EARLYCON_RISCV_SBI=y +CONFIG_SERIAL_MCTRL_GPIO=y +CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SERIAL_SIFIVE=y +CONFIG_SERIAL_SIFIVE_CONSOLE=y +CONFIG_SGL_ALLOC=y +CONFIG_SG_POOL=y +CONFIG_SIFIVE_CCACHE=y +CONFIG_SIFIVE_PLIC=y +CONFIG_SMP=y +# CONFIG_SND_SOC_STARFIVE is not set +CONFIG_SOCK_DIAG=y +CONFIG_SOCK_RX_QUEUE_MAPPING=y +# CONFIG_SOC_MICROCHIP_POLARFIRE is not set +CONFIG_SOC_SIFIVE=y +CONFIG_SOC_STARFIVE=y +# CONFIG_SOC_VIRT is not set +CONFIG_SOFTLOCKUP_DETECTOR=y +CONFIG_SOUND=y +CONFIG_SPARSEMEM_VMEMMAP_ENABLE=y +CONFIG_SPARSE_IRQ=y +CONFIG_SPI=y +CONFIG_SPI_CADENCE_QUADSPI=y +CONFIG_SPI_DYNAMIC=y +CONFIG_SPI_MASTER=y +CONFIG_SPI_MEM=y +CONFIG_SPI_SPIDEV=y +CONFIG_SRCU=y +CONFIG_STARFIVE_TIMER=y +CONFIG_STARFIVE_WATCHDOG=y +CONFIG_STMMAC_ETH=y +CONFIG_STMMAC_PLATFORM=y +CONFIG_STMMAC_SELFTESTS=y +CONFIG_SWIOTLB=y +CONFIG_SWPHY=y +CONFIG_SYNC_FILE=y +CONFIG_SYSCTL_EXCEPTION_TRACE=y +# CONFIG_SYSFB_SIMPLEFB is not set +CONFIG_THERMAL=y +CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y +CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS=0 +CONFIG_THERMAL_GOV_STEP_WISE=y +CONFIG_THERMAL_OF=y +CONFIG_THREAD_INFO_IN_TASK=y +CONFIG_TICK_CPU_ACCOUNTING=y +CONFIG_TIMER_OF=y +CONFIG_TIMER_PROBE=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_TOOLCHAIN_HAS_ZICBOM=y +CONFIG_TOOLCHAIN_HAS_ZIHINTPAUSE=y +CONFIG_TOOLCHAIN_NEEDS_EXPLICIT_ZICSR_ZIFENCEI=y +CONFIG_TREE_RCU=y +CONFIG_TREE_SRCU=y +CONFIG_TTY_PRINTK=y +CONFIG_TTY_PRINTK_LEVEL=6 +CONFIG_TUNE_GENERIC=y +CONFIG_UCS2_STRING=y +CONFIG_UNINLINE_SPIN_UNLOCK=y +CONFIG_USB=y +CONFIG_USB_COMMON=y +CONFIG_USB_CONFIGFS=y +# CONFIG_USB_CONFIGFS_ACM is not set +# CONFIG_USB_CONFIGFS_ECM is not set +# CONFIG_USB_CONFIGFS_ECM_SUBSET is not set +# CONFIG_USB_CONFIGFS_EEM is not set +CONFIG_USB_CONFIGFS_F_FS=y +# CONFIG_USB_CONFIGFS_F_HID is not set +# CONFIG_USB_CONFIGFS_F_LB_SS is not set +# CONFIG_USB_CONFIGFS_F_MIDI is not set +# CONFIG_USB_CONFIGFS_F_PRINTER is not set +# CONFIG_USB_CONFIGFS_F_UAC1 is not set +# CONFIG_USB_CONFIGFS_F_UAC1_LEGACY is not set +# CONFIG_USB_CONFIGFS_F_UAC2 is not set +# CONFIG_USB_CONFIGFS_F_UVC is not set +CONFIG_USB_CONFIGFS_MASS_STORAGE=y +# CONFIG_USB_CONFIGFS_NCM is not set +# CONFIG_USB_CONFIGFS_OBEX is not set +# CONFIG_USB_CONFIGFS_RNDIS is not set +# CONFIG_USB_CONFIGFS_SERIAL is not set +CONFIG_USB_F_FS=y +CONFIG_USB_F_MASS_STORAGE=y +CONFIG_USB_GADGET=y +CONFIG_USB_LIBCOMPOSITE=y +CONFIG_USB_PCI=y +CONFIG_USB_PHY=y +CONFIG_USB_ROLE_SWITCH=y +CONFIG_USB_SUPPORT=y +# CONFIG_USB_UHCI_HCD is not set +CONFIG_USELIB=y +CONFIG_USER_NS=y +CONFIG_VFAT_FS=y +# CONFIG_VIRTIO_BLK is not set +# CONFIG_VIRTIO_NET is not set +CONFIG_VMAP_STACK=y +CONFIG_WATCHDOG_CORE=y +CONFIG_WATCHDOG_SYSFS=y +CONFIG_WERROR=y +CONFIG_WQ_WATCHDOG=y +CONFIG_XPS=y +CONFIG_XXHASH=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZONE_DMA32=y From e2e2fc3cd0dd72b85a9d6bacbabb8937d9295dd3 Mon Sep 17 00:00:00 2001 From: Zoltan HERPAI Date: Sun, 1 Sep 2024 14:06:29 +0000 Subject: [PATCH 11/13] starfive: add patches for 6.6 Add updated patches for 6.6. DMA/cache-handling patches have been reworked / backported from upstream. Signed-off-by: Zoltan HERPAI --- ...110-sys-Fix-lower-rate-of-CPUfreq-by.patch | 76 + ...er-Add-timer-for-StarFive-JH7110-SoC.patch | 114 + ...-clocksource-Add-JH7110-timer-driver.patch | 428 + ...starfive-Remove-properties-from-requ.patch | 34 + ...tarfive-Change-tuning-implementation.patch | 199 + ...Add-bindings-for-OpenCores-PWM-Contr.patch | 79 + ...pwm-opencores-Add-PWM-driver-support.patch | 285 + ...remove-unnecessary-alignmask-for-aha.patch | 117 + ...-starfive-Update-driver-dependencies.patch | 25 + ...tarfive-RSA-poll-csr-for-done-status.patch | 200 + ...rypto-starfive-Pad-adata-with-zeroes.patch | 46 + ...2-crypto-starfive-Remove-cfb-and-ofb.patch | 125 + ...starfive-Remove-unneeded-NULL-checks.patch | 33 + ...Add-PLDA-XpressRICH-PCIe-host-common.patch | 183 + ...ve-pcie-microchip-host.c-to-plda-dir.patch | 2523 ++ ...ve-PLDA-IP-register-macros-to-pcie-p.patch | 259 + ...d-bridge_addr-field-to-struct-mc_pci.patch | 107 + ...chip-Rename-two-PCIe-data-structures.patch | 347 + ...ve-PCIe-host-data-structures-to-plda.patch | 87 + ...microchip-Rename-two-setup-functions.patch | 76 + ...ange-the-argument-of-plda_pcie_setup.patch | 49 + ...ve-setup-functions-to-pcie-plda-host.patch | 200 + ...p-Rename-interrupt-related-functions.patch | 336 + ...d-num_events-field-to-struct-plda_pc.patch | 65 + ...d-request_event_irq-callback-functio.patch | 114 + ...d-INTx-and-MSI-event-num-to-struct-p.patch | 56 + ...d-get_events-callback-and-add-PLDA-g.patch | 167 + ...d-event-irqchip-field-to-host-port-a.patch | 144 + ...ve-IRQ-functions-to-pcie-plda-host.c.patch | 1028 + ...nt-bitmap-field-to-struct-plda_pcie_.patch | 67 + ...st-init-deinit-and-map-bus-functions.patch | 256 + ...-Add-StarFive-JH7110-PCIe-controller.patch | 140 + ...ET_CONFIG_DEVICE_WAIT_MS-waiting-tim.patch | 55 + ...-starfive-Add-JH7110-PCIe-controller.patch | 623 + ...-Add-StarFive-JH7110-PWM-DAC-control.patch | 97 + ...C-starfive-Add-JH7110-PWM-DAC-driver.patch | 574 + ...C-Update-jh7110-PWM-DAC-for-ops-move.patch | 32 + ...7110-pwmdac-Convert-to-platform-remo.patch | 52 + ...r-Add-power-domain-header-for-JH7110.patch | 35 + ...e-Replace-SOC_STARFIVE-with-ARCH_STA.patch | 31 + ...e-Extract-JH7110-pmu-private-operati.patch | 179 + ...-starfive-Add-JH7110-AON-PMU-support.patch | 122 + ...-to-move-Kconfig-files-into-the-pmdo.patch | 36 + ...e-Move-Kconfig-file-to-the-pmdomain-.patch | 69 + ...r-Update-prefixes-for-AON-power-doma.patch | 31 + ...e-Update-prefixes-for-AON-power-doma.patch | 32 + ...ve-pinfunc-Fix-the-pins-name-of-I2ST.patch | 30 + ...ve-Add-full-support-except-VIN-and-V.patch | 549 + ...NTAINERS-Update-all-StarFive-entries.patch | 147 + ...nge-the-voltage-to-adapt-to-JH7110-E.patch | 64 + ...l022-Get-and-deassert-reset-in-probe.patch | 60 + ...-getting-platform-data-error-for-Sta.patch | 30 + ...-RX-master-support-for-StarFive-JH71.patch | 67 + ...e-jh7110-Unset-.strict-in-pinmux_ops.patch | 24 + ...ric.c-Export-symbol-__pte_offset_map.patch | 22 + ...-starfive-Add-JH7110-EVB-device-tree.patch | 2623 ++ ...ve-Add-JH7110-EVB-expanded-device-tr.patch | 728 + ...starfive-Add-evb-overlay-dtso-subdir.patch | 485 + ...onfigs-Add-starfive_jh7110_defconfig.patch | 385 + ...60-of-configfs-Add-configfs-function.patch | 318 + .../0061-usr-Add-gen_initramfs_list.sh.patch | 344 + ...ignware-Delete-SMBus-functionalities.patch | 33 + ...device-add-gd25lq256d-32M-flash-supp.patch | 26 + ...64-driver-mailbox-Add-mailbox-driver.patch | 808 + ...r-rtc-Add-StarFive-JH7110-rtc-driver.patch | 789 + ...t-8250-Add-dw-auto-flow-ctrl-support.patch | 96 + ...er-uart-fix-up-uart-communicate-fail.patch | 29 + ...50-add-reset-operation-in-runtime-PM.patch | 32 + ...bindings-CAN-Add-StarFive-CAN-module.patch | 113 + ...-CAN-starfive-Add-CAN-engine-support.patch | 1317 + ...ve-jh7110-Add-regulator-support-for-.patch | 204 + ...-precheck-and-delay-for-CQE-pending-.patch | 40 + ...te-unique-identification-for-SoC-PMU.patch | 93 + ...C-V-Support-CPUID-for-risc-v-in-perf.patch | 55 + ...C-V-Added-generic-pmu-events-mapfile.patch | 42 + ...erf-sbi-disable-cpu-hotplug-callback.patch | 30 + ...w-axi-dmac-Drop-unused-print-message.patch | 24 + ...9-ASoC-codecs-Add-AC108-Codec-driver.patch | 4748 +++ ...oC-starfive-Add-SPDIF-and-PCM-driver.patch | 1169 + ...-ASoC-starfive-Add-JH7110-PDM-driver.patch | 537 + ...082-dt-binding-input-Add-tink_ft5406.patch | 55 + ...een-Add-tinker_ft5406-driver-support.patch | 444 + ...ng-media-Add-JH7110-Camera-Subsystem.patch | 246 + ...edia-starfive-Add-vin-driver-support.patch | 24014 ++++++++++++++++ ...a-i2c-Add-IMX708-CMOS-sensor-binding.patch | 135 + .../0087-media-i2c-Add-imx708-support.patch | 1971 ++ .../0088-media-i2c-imx708-Delete-gain.patch | 69 + ...lay-Add-yamls-for-JH7110-display-sys.patch | 270 + ...1xx_pmu-Add-EVENT_TURN_OFF-register-.patch | 82 + ...orkqueue-Enable-flush_scheduled_work.patch | 22 + ...Optimize-memcpy-with-aligned-version.patch | 506 + ...Change-memcpy-to-the-aligned-version.patch | 37 + ...ols-remove-the-frame-SYNC-event-to-v.patch | 856 + ...fter-the-SC-buffer-for-the-AE-AWB-fl.patch | 249 + ...dd-ISP-control-for-video2-and-video3.patch | 117 + ...ia-starfive-Update-ISP-initialzation.patch | 226 + ...pto-jh7110-Comment-RSA-algo-register.patch | 36 + ...ve-jh7110-evb-Add-qspi-norflash-part.patch | 26 + ...lator-pmic-driver-support-kernel-6.6.patch | 22 + ...ve-Add-platform-bus-register-to-adap.patch | 232 + ...ve-Avoid-power-device-error-when-CON.patch | 92 + ...ve-fix-the-problem-of-spi-overlay-re.patch | 403 + ...ve-Enable-spi-to-be-compiled-into-mo.patch | 38 + ...d-visionfive2-defconfig-to-kernel-6..patch | 444 + ...ts-starfive-update-dts-to-kernel-6.6.patch | 759 + ...five-evb-overlay-Support-SPI-overlay.patch | 71 + ...sionfive2-Add-standard-partition-for.patch | 23 + ...ove-performance-usb-using-lowmem-for.patch | 297 + ...ma_alloc_noncoherent-to-alloc-low-me.patch | 106 + ...starfive-Add-vf2-overlay-dtso-subdir.patch | 139 + ...ve-visionfive-2-Add-aliases-for-i2c-.patch | 34 + ...bluetooth-add-aic8800-driver-support.patch | 6063 ++++ ...ve-visionfive-2-Sync-the-sound-card-.patch | 67 + ...110-Change-uart3-uart5-clk-register-.patch | 53 + ...ve-visionfive-2-Quote-corresponding-.patch | 32 + ...dd-starfive-jh7100-hsuart-compatible.patch | 25 + ...Add-StarFive-JH7100-audio-clock-node.patch | 32 + ...serial-8250-update-driver-for-JH7100.patch | 28 + ...-dmac-Handle-xfer-start-while-non-id.patch | 55 + ...axi-dmac-Add-StarFive-JH7100-support.patch | 64 + ...nctrl-starfive-Reset-pinmux-settings.patch | 127 + ...-flags-argument-to-JH71X0__MUX-macro.patch | 302 + ...100-Add-CLK_SET_RATE_PARENT-to-gmac_.patch | 25 + ...arfive-jh7100-Keep-more-clocks-alive.patch | 94 + .../1009-net-stmmac-use-GFP_DMA32.patch | 30 + ...ve-JH7100-Random-Number-Generator-dr.patch | 477 + ...sifive-ptc-Add-SiFive-PWM-PTC-driver.patch | 309 + ...t-Add-StarFive-JH7100-audio-reset-de.patch | 48 + ...t-Add-starfive-jh7100-audrst-binding.patch | 56 + ...arfive-Add-JH7100-audio-reset-driver.patch | 144 + ...Add-StarFive-JH7100-audio-reset-node.patch | 28 + ...e-ccache-Add-StarFive-JH7100-support.patch | 160 + ...cv-errata-Add-StarFive-JH7100-errata.patch | 44 + ...ve-Mark-the-JH7100-as-having-non-coh.patch | 26 + ...starfive-Add-JH7100-cache-controller.patch | 50 + ...ve-Add-pool-for-coherent-DMA-memory-.patch | 61 + ...cv-dts-starfive-Add-JH7100-MMC-nodes.patch | 49 + ...five-Enable-SD-card-on-JH7100-boards.patch | 85 + ...e-ERRATA_STARFIVE_JH7100-depend-on-D.patch | 33 + ...ll-JH7100-Starlight-and-VisionFive-s.patch | 1130 + 140 files changed, 67712 insertions(+) create mode 100644 target/linux/starfive/patches-6.6/0001-clk-starfive-jh7110-sys-Fix-lower-rate-of-CPUfreq-by.patch create mode 100644 target/linux/starfive/patches-6.6/0002-dt-bindings-timer-Add-timer-for-StarFive-JH7110-SoC.patch create mode 100644 target/linux/starfive/patches-6.6/0003-clocksource-Add-JH7110-timer-driver.patch create mode 100644 target/linux/starfive/patches-6.6/0004-dt-bindings-mmc-starfive-Remove-properties-from-requ.patch create mode 100644 target/linux/starfive/patches-6.6/0005-mmc-starfive-Change-tuning-implementation.patch create mode 100644 target/linux/starfive/patches-6.6/0006-dt-bindings-pwm-Add-bindings-for-OpenCores-PWM-Contr.patch create mode 100644 target/linux/starfive/patches-6.6/0007-pwm-opencores-Add-PWM-driver-support.patch create mode 100644 target/linux/starfive/patches-6.6/0008-crypto-starfive-remove-unnecessary-alignmask-for-aha.patch create mode 100644 target/linux/starfive/patches-6.6/0009-crypto-starfive-Update-driver-dependencies.patch create mode 100644 target/linux/starfive/patches-6.6/0010-crypto-starfive-RSA-poll-csr-for-done-status.patch create mode 100644 target/linux/starfive/patches-6.6/0011-crypto-starfive-Pad-adata-with-zeroes.patch create mode 100644 target/linux/starfive/patches-6.6/0012-crypto-starfive-Remove-cfb-and-ofb.patch create mode 100644 target/linux/starfive/patches-6.6/0013-crypto-starfive-Remove-unneeded-NULL-checks.patch create mode 100644 target/linux/starfive/patches-6.6/0014-dt-bindings-PCI-Add-PLDA-XpressRICH-PCIe-host-common.patch create mode 100644 target/linux/starfive/patches-6.6/0015-PCI-microchip-Move-pcie-microchip-host.c-to-plda-dir.patch create mode 100644 target/linux/starfive/patches-6.6/0016-PCI-microchip-Move-PLDA-IP-register-macros-to-pcie-p.patch create mode 100644 target/linux/starfive/patches-6.6/0017-PCI-microchip-Add-bridge_addr-field-to-struct-mc_pci.patch create mode 100644 target/linux/starfive/patches-6.6/0018-PCI-microchip-Rename-two-PCIe-data-structures.patch create mode 100644 target/linux/starfive/patches-6.6/0019-PCI-microchip-Move-PCIe-host-data-structures-to-plda.patch create mode 100644 target/linux/starfive/patches-6.6/0020-PCI-microchip-Rename-two-setup-functions.patch create mode 100644 target/linux/starfive/patches-6.6/0021-PCI-microchip-Change-the-argument-of-plda_pcie_setup.patch create mode 100644 target/linux/starfive/patches-6.6/0022-PCI-microchip-Move-setup-functions-to-pcie-plda-host.patch create mode 100644 target/linux/starfive/patches-6.6/0023-PCI-microchip-Rename-interrupt-related-functions.patch create mode 100644 target/linux/starfive/patches-6.6/0024-PCI-microchip-Add-num_events-field-to-struct-plda_pc.patch create mode 100644 target/linux/starfive/patches-6.6/0025-PCI-microchip-Add-request_event_irq-callback-functio.patch create mode 100644 target/linux/starfive/patches-6.6/0026-PCI-microchip-Add-INTx-and-MSI-event-num-to-struct-p.patch create mode 100644 target/linux/starfive/patches-6.6/0027-PCI-microchip-Add-get_events-callback-and-add-PLDA-g.patch create mode 100644 target/linux/starfive/patches-6.6/0028-PCI-microchip-Add-event-irqchip-field-to-host-port-a.patch create mode 100644 target/linux/starfive/patches-6.6/0029-PCI-microchip-Move-IRQ-functions-to-pcie-plda-host.c.patch create mode 100644 target/linux/starfive/patches-6.6/0030-pci-plda-Add-event-bitmap-field-to-struct-plda_pcie_.patch create mode 100644 target/linux/starfive/patches-6.6/0031-PCI-plda-Add-host-init-deinit-and-map-bus-functions.patch create mode 100644 target/linux/starfive/patches-6.6/0032-dt-bindings-PCI-Add-StarFive-JH7110-PCIe-controller.patch create mode 100644 target/linux/starfive/patches-6.6/0033-PCI-Add-PCIE_RESET_CONFIG_DEVICE_WAIT_MS-waiting-tim.patch create mode 100644 target/linux/starfive/patches-6.6/0034-PCI-starfive-Add-JH7110-PCIe-controller.patch create mode 100644 target/linux/starfive/patches-6.6/0035-ASoC-dt-bindings-Add-StarFive-JH7110-PWM-DAC-control.patch create mode 100644 target/linux/starfive/patches-6.6/0036-ASoC-starfive-Add-JH7110-PWM-DAC-driver.patch create mode 100644 target/linux/starfive/patches-6.6/0037-ASoC-Update-jh7110-PWM-DAC-for-ops-move.patch create mode 100644 target/linux/starfive/patches-6.6/0038-ASoC-starfive-jh7110-pwmdac-Convert-to-platform-remo.patch create mode 100644 target/linux/starfive/patches-6.6/0039-dt-bindings-power-Add-power-domain-header-for-JH7110.patch create mode 100644 target/linux/starfive/patches-6.6/0040-pmdomain-starfive-Replace-SOC_STARFIVE-with-ARCH_STA.patch create mode 100644 target/linux/starfive/patches-6.6/0041-pmdomain-starfive-Extract-JH7110-pmu-private-operati.patch create mode 100644 target/linux/starfive/patches-6.6/0042-pmdomain-starfive-Add-JH7110-AON-PMU-support.patch create mode 100644 target/linux/starfive/patches-6.6/0043-pmdomain-Prepare-to-move-Kconfig-files-into-the-pmdo.patch create mode 100644 target/linux/starfive/patches-6.6/0044-pmdomain-starfive-Move-Kconfig-file-to-the-pmdomain-.patch create mode 100644 target/linux/starfive/patches-6.6/0045-dt-bindings-power-Update-prefixes-for-AON-power-doma.patch create mode 100644 target/linux/starfive/patches-6.6/0046-pmdomain-starfive-Update-prefixes-for-AON-power-doma.patch create mode 100644 target/linux/starfive/patches-6.6/0047-riscv-dts-starfive-pinfunc-Fix-the-pins-name-of-I2ST.patch create mode 100644 target/linux/starfive/patches-6.6/0048-riscv-dts-starfive-Add-full-support-except-VIN-and-V.patch create mode 100644 target/linux/starfive/patches-6.6/0049-MAINTAINERS-Update-all-StarFive-entries.patch create mode 100644 target/linux/starfive/patches-6.6/0050-mmc-starfive-Change-the-voltage-to-adapt-to-JH7110-E.patch create mode 100644 target/linux/starfive/patches-6.6/0051-spi-spl022-Get-and-deassert-reset-in-probe.patch create mode 100644 target/linux/starfive/patches-6.6/0052-ASoC-dwc-i2s-Fix-getting-platform-data-error-for-Sta.patch create mode 100644 target/linux/starfive/patches-6.6/0053-ASoC-dwc-i2s-Add-RX-master-support-for-StarFive-JH71.patch create mode 100644 target/linux/starfive/patches-6.6/0054-pinctrl-starfive-jh7110-Unset-.strict-in-pinmux_ops.patch create mode 100644 target/linux/starfive/patches-6.6/0055-mm-pgtable-generic.c-Export-symbol-__pte_offset_map.patch create mode 100644 target/linux/starfive/patches-6.6/0056-riscv-dts-starfive-Add-JH7110-EVB-device-tree.patch create mode 100644 target/linux/starfive/patches-6.6/0057-riscv-dts-starfive-Add-JH7110-EVB-expanded-device-tr.patch create mode 100644 target/linux/starfive/patches-6.6/0058-riscv-dts-starfive-Add-evb-overlay-dtso-subdir.patch create mode 100644 target/linux/starfive/patches-6.6/0059-riscv-configs-Add-starfive_jh7110_defconfig.patch create mode 100644 target/linux/starfive/patches-6.6/0060-of-configfs-Add-configfs-function.patch create mode 100644 target/linux/starfive/patches-6.6/0061-usr-Add-gen_initramfs_list.sh.patch create mode 100644 target/linux/starfive/patches-6.6/0062-i2c-designware-Delete-SMBus-functionalities.patch create mode 100644 target/linux/starfive/patches-6.6/0063-drivers-mtd-gigadevice-add-gd25lq256d-32M-flash-supp.patch create mode 100644 target/linux/starfive/patches-6.6/0064-driver-mailbox-Add-mailbox-driver.patch create mode 100644 target/linux/starfive/patches-6.6/0065-driver-rtc-Add-StarFive-JH7110-rtc-driver.patch create mode 100644 target/linux/starfive/patches-6.6/0066-uart-8250-Add-dw-auto-flow-ctrl-support.patch create mode 100644 target/linux/starfive/patches-6.6/0067-driver-uart-fix-up-uart-communicate-fail.patch create mode 100644 target/linux/starfive/patches-6.6/0068-uart-8250-add-reset-operation-in-runtime-PM.patch create mode 100644 target/linux/starfive/patches-6.6/0069-dt-bindings-CAN-Add-StarFive-CAN-module.patch create mode 100644 target/linux/starfive/patches-6.6/0070-CAN-starfive-Add-CAN-engine-support.patch create mode 100644 target/linux/starfive/patches-6.6/0071-regulator-starfive-jh7110-Add-regulator-support-for-.patch create mode 100644 target/linux/starfive/patches-6.6/0072-drivers-nvme-Add-precheck-and-delay-for-CQE-pending-.patch create mode 100644 target/linux/starfive/patches-6.6/0073-RISC-V-Create-unique-identification-for-SoC-PMU.patch create mode 100644 target/linux/starfive/patches-6.6/0074-RISC-V-Support-CPUID-for-risc-v-in-perf.patch create mode 100644 target/linux/starfive/patches-6.6/0075-RISC-V-Added-generic-pmu-events-mapfile.patch create mode 100644 target/linux/starfive/patches-6.6/0076-perf-sbi-disable-cpu-hotplug-callback.patch create mode 100644 target/linux/starfive/patches-6.6/0077-dmaengine-dw-axi-dmac-Drop-unused-print-message.patch create mode 100644 target/linux/starfive/patches-6.6/0079-ASoC-codecs-Add-AC108-Codec-driver.patch create mode 100644 target/linux/starfive/patches-6.6/0080-ASoC-starfive-Add-SPDIF-and-PCM-driver.patch create mode 100644 target/linux/starfive/patches-6.6/0081-ASoC-starfive-Add-JH7110-PDM-driver.patch create mode 100644 target/linux/starfive/patches-6.6/0082-dt-binding-input-Add-tink_ft5406.patch create mode 100644 target/linux/starfive/patches-6.6/0083-input-touchscreen-Add-tinker_ft5406-driver-support.patch create mode 100644 target/linux/starfive/patches-6.6/0084-dt-binding-media-Add-JH7110-Camera-Subsystem.patch create mode 100644 target/linux/starfive/patches-6.6/0085-media-starfive-Add-vin-driver-support.patch create mode 100644 target/linux/starfive/patches-6.6/0086-dt-bindings-media-i2c-Add-IMX708-CMOS-sensor-binding.patch create mode 100644 target/linux/starfive/patches-6.6/0087-media-i2c-Add-imx708-support.patch create mode 100644 target/linux/starfive/patches-6.6/0088-media-i2c-imx708-Delete-gain.patch create mode 100644 target/linux/starfive/patches-6.6/0089-dt-bindings-display-Add-yamls-for-JH7110-display-sys.patch create mode 100644 target/linux/starfive/patches-6.6/0090-soc-starfive-jh71xx_pmu-Add-EVENT_TURN_OFF-register-.patch create mode 100644 target/linux/starfive/patches-6.6/0091-workqueue-Enable-flush_scheduled_work.patch create mode 100644 target/linux/starfive/patches-6.6/0092-riscv-Optimize-memcpy-with-aligned-version.patch create mode 100644 target/linux/starfive/patches-6.6/0093-riscv-purgatory-Change-memcpy-to-the-aligned-version.patch create mode 100644 target/linux/starfive/patches-6.6/0094-Add-16-ISP-controls-remove-the-frame-SYNC-event-to-v.patch create mode 100644 target/linux/starfive/patches-6.6/0095-Expand-2-bytes-after-the-SC-buffer-for-the-AE-AWB-fl.patch create mode 100644 target/linux/starfive/patches-6.6/0096-Add-ISP-control-for-video2-and-video3.patch create mode 100644 target/linux/starfive/patches-6.6/0097-media-starfive-Update-ISP-initialzation.patch create mode 100644 target/linux/starfive/patches-6.6/0098-crypto-jh7110-Comment-RSA-algo-register.patch create mode 100644 target/linux/starfive/patches-6.6/0099-riscv-dts-starfive-jh7110-evb-Add-qspi-norflash-part.patch create mode 100644 target/linux/starfive/patches-6.6/0100-driver-regulator-pmic-driver-support-kernel-6.6.patch create mode 100644 target/linux/starfive/patches-6.6/0101-spi-pl022-starfive-Add-platform-bus-register-to-adap.patch create mode 100644 target/linux/starfive/patches-6.6/0102-spi-pl022-starfive-Avoid-power-device-error-when-CON.patch create mode 100644 target/linux/starfive/patches-6.6/0103-spi-pl022-starfive-fix-the-problem-of-spi-overlay-re.patch create mode 100644 target/linux/starfive/patches-6.6/0104-spi-pl022-starfive-Enable-spi-to-be-compiled-into-mo.patch create mode 100644 target/linux/starfive/patches-6.6/0105-riscv-configs-add-visionfive2-defconfig-to-kernel-6..patch create mode 100644 target/linux/starfive/patches-6.6/0106-riscv-dts-starfive-update-dts-to-kernel-6.6.patch create mode 100644 target/linux/starfive/patches-6.6/0107-riscv-dts-starfive-evb-overlay-Support-SPI-overlay.patch create mode 100644 target/linux/starfive/patches-6.6/0108-riscv-configs-visionfive2-Add-standard-partition-for.patch create mode 100644 target/linux/starfive/patches-6.6/0109-usb-xhci-To-improve-performance-usb-using-lowmem-for.patch create mode 100644 target/linux/starfive/patches-6.6/0110-usb-xhci-using-dma_alloc_noncoherent-to-alloc-low-me.patch create mode 100644 target/linux/starfive/patches-6.6/0111-riscv-dts-starfive-Add-vf2-overlay-dtso-subdir.patch create mode 100644 target/linux/starfive/patches-6.6/0112-riscv-dts-starfive-visionfive-2-Add-aliases-for-i2c-.patch create mode 100644 target/linux/starfive/patches-6.6/0113-driver-bluetooth-add-aic8800-driver-support.patch create mode 100644 target/linux/starfive/patches-6.6/0114-riscv-dts-starfive-visionfive-2-Sync-the-sound-card-.patch create mode 100644 target/linux/starfive/patches-6.6/0115-clk-starfive-jh7110-Change-uart3-uart5-clk-register-.patch create mode 100644 target/linux/starfive/patches-6.6/0116-riscv-dts-starfive-visionfive-2-Quote-corresponding-.patch create mode 100644 target/linux/starfive/patches-6.6/1000-serial-8250_dw-Add-starfive-jh7100-hsuart-compatible.patch create mode 100644 target/linux/starfive/patches-6.6/1001-RISC-V-Add-StarFive-JH7100-audio-clock-node.patch create mode 100644 target/linux/starfive/patches-6.6/1002-drivers-tty-serial-8250-update-driver-for-JH7100.patch create mode 100644 target/linux/starfive/patches-6.6/1003-dmaengine-dw-axi-dmac-Handle-xfer-start-while-non-id.patch create mode 100644 target/linux/starfive/patches-6.6/1004-dmaengine-dw-axi-dmac-Add-StarFive-JH7100-support.patch create mode 100644 target/linux/starfive/patches-6.6/1005-pinctrl-starfive-Reset-pinmux-settings.patch create mode 100644 target/linux/starfive/patches-6.6/1006-clk-starfive-Add-flags-argument-to-JH71X0__MUX-macro.patch create mode 100644 target/linux/starfive/patches-6.6/1007-clk-starfive-jh7100-Add-CLK_SET_RATE_PARENT-to-gmac_.patch create mode 100644 target/linux/starfive/patches-6.6/1008-clk-starfive-jh7100-Keep-more-clocks-alive.patch create mode 100644 target/linux/starfive/patches-6.6/1009-net-stmmac-use-GFP_DMA32.patch create mode 100644 target/linux/starfive/patches-6.6/1010-hwrng-Add-StarFive-JH7100-Random-Number-Generator-dr.patch create mode 100644 target/linux/starfive/patches-6.6/1011-pwm-sifive-ptc-Add-SiFive-PWM-PTC-driver.patch create mode 100644 target/linux/starfive/patches-6.6/1012-dt-bindings-reset-Add-StarFive-JH7100-audio-reset-de.patch create mode 100644 target/linux/starfive/patches-6.6/1013-dt-bindings-reset-Add-starfive-jh7100-audrst-binding.patch create mode 100644 target/linux/starfive/patches-6.6/1014-reset-starfive-Add-JH7100-audio-reset-driver.patch create mode 100644 target/linux/starfive/patches-6.6/1015-RISC-V-Add-StarFive-JH7100-audio-reset-node.patch create mode 100644 target/linux/starfive/patches-6.6/1016-soc-sifive-ccache-Add-StarFive-JH7100-support.patch create mode 100644 target/linux/starfive/patches-6.6/1017-riscv-errata-Add-StarFive-JH7100-errata.patch create mode 100644 target/linux/starfive/patches-6.6/1018-riscv-dts-starfive-Mark-the-JH7100-as-having-non-coh.patch create mode 100644 target/linux/starfive/patches-6.6/1019-riscv-dts-starfive-Add-JH7100-cache-controller.patch create mode 100644 target/linux/starfive/patches-6.6/1020-riscv-dts-starfive-Add-pool-for-coherent-DMA-memory-.patch create mode 100644 target/linux/starfive/patches-6.6/1021-riscv-dts-starfive-Add-JH7100-MMC-nodes.patch create mode 100644 target/linux/starfive/patches-6.6/1022-riscv-dts-starfive-Enable-SD-card-on-JH7100-boards.patch create mode 100644 target/linux/starfive/patches-6.6/1023-riscv-errata-Make-ERRATA_STARFIVE_JH7100-depend-on-D.patch create mode 100644 target/linux/starfive/patches-6.6/1024-riscv-dts-Add-full-JH7100-Starlight-and-VisionFive-s.patch diff --git a/target/linux/starfive/patches-6.6/0001-clk-starfive-jh7110-sys-Fix-lower-rate-of-CPUfreq-by.patch b/target/linux/starfive/patches-6.6/0001-clk-starfive-jh7110-sys-Fix-lower-rate-of-CPUfreq-by.patch new file mode 100644 index 0000000000..d21f5efebb --- /dev/null +++ b/target/linux/starfive/patches-6.6/0001-clk-starfive-jh7110-sys-Fix-lower-rate-of-CPUfreq-by.patch @@ -0,0 +1,76 @@ +From 69275b667bd930cf5d5f577ba0ab1987c9d13987 Mon Sep 17 00:00:00 2001 +From: Xingyu Wu +Date: Mon, 21 Aug 2023 23:29:15 +0800 +Subject: [PATCH 001/116] clk: starfive: jh7110-sys: Fix lower rate of CPUfreq + by setting PLL0 rate to 1.5GHz + +CPUfreq supports 4 cpu frequency loads on 375/500/750/1500MHz. +But now PLL0 rate is 1GHz and the cpu frequency loads become +333/500/500/1000MHz in fact. + +So PLL0 rate should be set to 1.5GHz. Change the parent of cpu_root clock +and the divider of cpu_core before the setting. + +Reviewed-by: Hal Feng +Fixes: e2c510d6d630 ("riscv: dts: starfive: Add cpu scaling for JH7110 SoC") +Signed-off-by: Xingyu Wu +--- + .../clk/starfive/clk-starfive-jh7110-sys.c | 47 ++++++++++++++++++- + 1 file changed, 46 insertions(+), 1 deletion(-) + +--- a/drivers/clk/starfive/clk-starfive-jh7110-sys.c ++++ b/drivers/clk/starfive/clk-starfive-jh7110-sys.c +@@ -501,7 +501,52 @@ static int __init jh7110_syscrg_probe(st + if (ret) + return ret; + +- return jh7110_reset_controller_register(priv, "rst-sys", 0); ++ ret = jh7110_reset_controller_register(priv, "rst-sys", 0); ++ if (ret) ++ return ret; ++ ++ /* ++ * Set PLL0 rate to 1.5GHz ++ * In order to not affect the cpu when the PLL0 rate is changing, ++ * we need to switch the parent of cpu_root clock to osc clock first, ++ * and then switch back after setting the PLL0 rate. ++ */ ++ pllclk = clk_get(priv->dev, "pll0_out"); ++ if (!IS_ERR(pllclk)) { ++ struct clk *osc = clk_get(&pdev->dev, "osc"); ++ struct clk *cpu_root = priv->reg[JH7110_SYSCLK_CPU_ROOT].hw.clk; ++ struct clk *cpu_core = priv->reg[JH7110_SYSCLK_CPU_CORE].hw.clk; ++ ++ if (IS_ERR(osc)) { ++ clk_put(pllclk); ++ return PTR_ERR(osc); ++ } ++ ++ /* ++ * CPU need voltage regulation by CPUfreq if set 1.5GHz. ++ * So in this driver, cpu_core need to be set the divider to be 2 first ++ * and will be 750M after setting parent. ++ */ ++ ret = clk_set_rate(cpu_core, clk_get_rate(cpu_core) / 2); ++ if (ret) ++ goto failed_set; ++ ++ ret = clk_set_parent(cpu_root, osc); ++ if (ret) ++ goto failed_set; ++ ++ ret = clk_set_rate(pllclk, 1500000000); ++ if (ret) ++ goto failed_set; ++ ++ ret = clk_set_parent(cpu_root, pllclk); ++ ++failed_set: ++ clk_put(pllclk); ++ clk_put(osc); ++ } ++ ++ return ret; + } + + static const struct of_device_id jh7110_syscrg_match[] = { diff --git a/target/linux/starfive/patches-6.6/0002-dt-bindings-timer-Add-timer-for-StarFive-JH7110-SoC.patch b/target/linux/starfive/patches-6.6/0002-dt-bindings-timer-Add-timer-for-StarFive-JH7110-SoC.patch new file mode 100644 index 0000000000..2b5318bb44 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0002-dt-bindings-timer-Add-timer-for-StarFive-JH7110-SoC.patch @@ -0,0 +1,114 @@ +From 7d0dbcbc079e4f72b69f53442b7759da6ebc4f87 Mon Sep 17 00:00:00 2001 +From: Xingyu Wu +Date: Thu, 19 Oct 2023 13:34:59 +0800 +Subject: [PATCH 002/116] dt-bindings: timer: Add timer for StarFive JH7110 SoC + +Add bindings for the timer on the JH7110 RISC-V SoC +by StarFive Technology Ltd. + +Reviewed-by: Krzysztof Kozlowski +Signed-off-by: Xingyu Wu +--- + .../bindings/timer/starfive,jh7110-timer.yaml | 96 +++++++++++++++++++ + 1 file changed, 96 insertions(+) + create mode 100644 Documentation/devicetree/bindings/timer/starfive,jh7110-timer.yaml + +--- /dev/null ++++ b/Documentation/devicetree/bindings/timer/starfive,jh7110-timer.yaml +@@ -0,0 +1,96 @@ ++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/timer/starfive,jh7110-timer.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: StarFive JH7110 Timer ++ ++maintainers: ++ - Xingyu Wu ++ - Samin Guo ++ ++description: ++ This timer has four free-running 32 bit counters in StarFive JH7110 SoC. ++ And each channel(counter) triggers an interrupt when timeout. They support ++ one-shot mode and continuous-run mode. ++ ++properties: ++ compatible: ++ const: starfive,jh7110-timer ++ ++ reg: ++ maxItems: 1 ++ ++ interrupts: ++ items: ++ - description: channel 0 ++ - description: channel 1 ++ - description: channel 2 ++ - description: channel 3 ++ ++ clocks: ++ items: ++ - description: timer APB ++ - description: channel 0 ++ - description: channel 1 ++ - description: channel 2 ++ - description: channel 3 ++ ++ clock-names: ++ items: ++ - const: apb ++ - const: ch0 ++ - const: ch1 ++ - const: ch2 ++ - const: ch3 ++ ++ resets: ++ items: ++ - description: timer APB ++ - description: channel 0 ++ - description: channel 1 ++ - description: channel 2 ++ - description: channel 3 ++ ++ reset-names: ++ items: ++ - const: apb ++ - const: ch0 ++ - const: ch1 ++ - const: ch2 ++ - const: ch3 ++ ++required: ++ - compatible ++ - reg ++ - interrupts ++ - clocks ++ - clock-names ++ - resets ++ - reset-names ++ ++additionalProperties: false ++ ++examples: ++ - | ++ timer@13050000 { ++ compatible = "starfive,jh7110-timer"; ++ reg = <0x13050000 0x10000>; ++ interrupts = <69>, <70>, <71> ,<72>; ++ clocks = <&clk 124>, ++ <&clk 125>, ++ <&clk 126>, ++ <&clk 127>, ++ <&clk 128>; ++ clock-names = "apb", "ch0", "ch1", ++ "ch2", "ch3"; ++ resets = <&rst 117>, ++ <&rst 118>, ++ <&rst 119>, ++ <&rst 120>, ++ <&rst 121>; ++ reset-names = "apb", "ch0", "ch1", ++ "ch2", "ch3"; ++ }; ++ diff --git a/target/linux/starfive/patches-6.6/0003-clocksource-Add-JH7110-timer-driver.patch b/target/linux/starfive/patches-6.6/0003-clocksource-Add-JH7110-timer-driver.patch new file mode 100644 index 0000000000..68b9c38d5b --- /dev/null +++ b/target/linux/starfive/patches-6.6/0003-clocksource-Add-JH7110-timer-driver.patch @@ -0,0 +1,428 @@ +From 7cb47848f8a10aed6e050c0ea483b4bb5eaa62a4 Mon Sep 17 00:00:00 2001 +From: Xingyu Wu +Date: Thu, 19 Oct 2023 13:35:00 +0800 +Subject: [PATCH 003/116] clocksource: Add JH7110 timer driver + +Add timer driver for the StarFive JH7110 SoC. + +Signed-off-by: Xingyu Wu +--- + drivers/clocksource/Kconfig | 11 + + drivers/clocksource/Makefile | 1 + + drivers/clocksource/timer-jh7110.c | 380 +++++++++++++++++++++++++++++ + 3 files changed, 392 insertions(+) + create mode 100644 drivers/clocksource/timer-jh7110.c + +--- a/drivers/clocksource/Kconfig ++++ b/drivers/clocksource/Kconfig +@@ -641,6 +641,17 @@ config RISCV_TIMER + is accessed via both the SBI and the rdcycle instruction. This is + required for all RISC-V systems. + ++config STARFIVE_JH7110_TIMER ++ bool "Timer for the STARFIVE JH7110 SoC" ++ depends on ARCH_STARFIVE || COMPILE_TEST ++ select TIMER_OF ++ select CLKSRC_MMIO ++ default ARCH_STARFIVE ++ help ++ This enables the timer for StarFive JH7110 SoC. On RISC-V platform, ++ the system has started RISCV_TIMER, but you can also use this timer ++ which can provide four channels to do a lot more things on JH7110 SoC. ++ + config CLINT_TIMER + bool "CLINT Timer for the RISC-V platform" if COMPILE_TEST + depends on GENERIC_SCHED_CLOCK && RISCV +--- a/drivers/clocksource/Makefile ++++ b/drivers/clocksource/Makefile +@@ -80,6 +80,7 @@ obj-$(CONFIG_INGENIC_TIMER) += ingenic- + obj-$(CONFIG_CLKSRC_ST_LPC) += clksrc_st_lpc.o + obj-$(CONFIG_X86_NUMACHIP) += numachip.o + obj-$(CONFIG_RISCV_TIMER) += timer-riscv.o ++obj-$(CONFIG_STARFIVE_JH7110_TIMER) += timer-jh7110.o + obj-$(CONFIG_CLINT_TIMER) += timer-clint.o + obj-$(CONFIG_CSKY_MP_TIMER) += timer-mp-csky.o + obj-$(CONFIG_GX6605S_TIMER) += timer-gx6605s.o +--- /dev/null ++++ b/drivers/clocksource/timer-jh7110.c +@@ -0,0 +1,380 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Starfive JH7110 Timer driver ++ * ++ * Copyright (C) 2022-2023 StarFive Technology Co., Ltd. ++ * ++ * Author: ++ * Xingyu Wu ++ * Samin Guo ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* Bias: Ch0-0x0, Ch1-0x40, Ch2-0x80, and so on. */ ++#define JH7110_TIMER_CH_LEN 0x40 ++#define JH7110_TIMER_CH_BASE(x) ((x) * JH7110_TIMER_CH_LEN) ++#define JH7110_TIMER_CH_MAX 4 ++ ++#define JH7110_CLOCK_SOURCE_RATING 200 ++#define JH7110_VALID_BITS 32 ++#define JH7110_DELAY_US 0 ++#define JH7110_TIMEOUT_US 10000 ++#define JH7110_CLOCKEVENT_RATING 300 ++#define JH7110_TIMER_MAX_TICKS 0xffffffff ++#define JH7110_TIMER_MIN_TICKS 0xf ++#define JH7110_TIMER_RELOAD_VALUE 0 ++ ++#define JH7110_TIMER_INT_STATUS 0x00 /* RO[0:4]: Interrupt Status for channel0~4 */ ++#define JH7110_TIMER_CTL 0x04 /* RW[0]: 0-continuous run, 1-single run */ ++#define JH7110_TIMER_LOAD 0x08 /* RW: load value to counter */ ++#define JH7110_TIMER_ENABLE 0x10 /* RW[0]: timer enable register */ ++#define JH7110_TIMER_RELOAD 0x14 /* RW: write 1 or 0 both reload counter */ ++#define JH7110_TIMER_VALUE 0x18 /* RO: timer value register */ ++#define JH7110_TIMER_INT_CLR 0x20 /* RW: timer interrupt clear register */ ++#define JH7110_TIMER_INT_MASK 0x24 /* RW[0]: timer interrupt mask register */ ++ ++#define JH7110_TIMER_INT_CLR_ENA BIT(0) ++#define JH7110_TIMER_INT_CLR_AVA_MASK BIT(1) ++ ++struct jh7110_clkevt { ++ struct clock_event_device evt; ++ struct clocksource cs; ++ bool cs_is_valid; ++ struct clk *clk; ++ struct reset_control *rst; ++ u32 rate; ++ u32 reload_val; ++ void __iomem *base; ++ char name[sizeof("jh7110-timer.chX")]; ++}; ++ ++struct jh7110_timer_priv { ++ struct clk *pclk; ++ struct reset_control *prst; ++ struct jh7110_clkevt clkevt[JH7110_TIMER_CH_MAX]; ++}; ++ ++/* 0:continuous-run mode, 1:single-run mode */ ++enum jh7110_timer_mode { ++ JH7110_TIMER_MODE_CONTIN, ++ JH7110_TIMER_MODE_SINGLE, ++}; ++ ++/* Interrupt Mask, 0:Unmask, 1:Mask */ ++enum jh7110_timer_int_mask { ++ JH7110_TIMER_INT_ENA, ++ JH7110_TIMER_INT_DIS, ++}; ++ ++enum jh7110_timer_enable { ++ JH7110_TIMER_DIS, ++ JH7110_TIMER_ENA, ++}; ++ ++static inline struct jh7110_clkevt *to_jh7110_clkevt(struct clock_event_device *evt) ++{ ++ return container_of(evt, struct jh7110_clkevt, evt); ++} ++ ++/* ++ * BIT(0): Read value represent channel int status. ++ * Write 1 to this bit to clear interrupt. Write 0 has no effects. ++ * BIT(1): "1" means that it is clearing interrupt. BIT(0) can not be written. ++ */ ++static inline int jh7110_timer_int_clear(struct jh7110_clkevt *clkevt) ++{ ++ u32 value; ++ int ret; ++ ++ /* Waiting interrupt can be cleared */ ++ ret = readl_poll_timeout_atomic(clkevt->base + JH7110_TIMER_INT_CLR, value, ++ !(value & JH7110_TIMER_INT_CLR_AVA_MASK), ++ JH7110_DELAY_US, JH7110_TIMEOUT_US); ++ if (!ret) ++ writel(JH7110_TIMER_INT_CLR_ENA, clkevt->base + JH7110_TIMER_INT_CLR); ++ ++ return ret; ++} ++ ++static int jh7110_timer_start(struct jh7110_clkevt *clkevt) ++{ ++ int ret; ++ ++ /* Disable and clear interrupt first */ ++ writel(JH7110_TIMER_INT_DIS, clkevt->base + JH7110_TIMER_INT_MASK); ++ ret = jh7110_timer_int_clear(clkevt); ++ if (ret) ++ return ret; ++ ++ writel(JH7110_TIMER_INT_ENA, clkevt->base + JH7110_TIMER_INT_MASK); ++ writel(JH7110_TIMER_ENA, clkevt->base + JH7110_TIMER_ENABLE); ++ ++ return 0; ++} ++ ++static int jh7110_timer_shutdown(struct clock_event_device *evt) ++{ ++ struct jh7110_clkevt *clkevt = to_jh7110_clkevt(evt); ++ ++ writel(JH7110_TIMER_DIS, clkevt->base + JH7110_TIMER_ENABLE); ++ return jh7110_timer_int_clear(clkevt); ++} ++ ++static void jh7110_timer_suspend(struct clock_event_device *evt) ++{ ++ struct jh7110_clkevt *clkevt = to_jh7110_clkevt(evt); ++ ++ clkevt->reload_val = readl(clkevt->base + JH7110_TIMER_LOAD); ++ jh7110_timer_shutdown(evt); ++} ++ ++static void jh7110_timer_resume(struct clock_event_device *evt) ++{ ++ struct jh7110_clkevt *clkevt = to_jh7110_clkevt(evt); ++ ++ writel(clkevt->reload_val, clkevt->base + JH7110_TIMER_LOAD); ++ writel(JH7110_TIMER_RELOAD_VALUE, clkevt->base + JH7110_TIMER_RELOAD); ++ jh7110_timer_start(clkevt); ++} ++ ++static int jh7110_timer_tick_resume(struct clock_event_device *evt) ++{ ++ jh7110_timer_resume(evt); ++ ++ return 0; ++} ++ ++/* IRQ handler for the timer */ ++static irqreturn_t jh7110_timer_interrupt(int irq, void *priv) ++{ ++ struct clock_event_device *evt = (struct clock_event_device *)priv; ++ struct jh7110_clkevt *clkevt = to_jh7110_clkevt(evt); ++ ++ if (jh7110_timer_int_clear(clkevt)) ++ return IRQ_NONE; ++ ++ if (evt->event_handler) ++ evt->event_handler(evt); ++ ++ return IRQ_HANDLED; ++} ++ ++static int jh7110_timer_set_periodic(struct clock_event_device *evt) ++{ ++ struct jh7110_clkevt *clkevt = to_jh7110_clkevt(evt); ++ u32 periodic = DIV_ROUND_CLOSEST(clkevt->rate, HZ); ++ ++ writel(JH7110_TIMER_MODE_CONTIN, clkevt->base + JH7110_TIMER_CTL); ++ writel(periodic, clkevt->base + JH7110_TIMER_LOAD); ++ ++ return jh7110_timer_start(clkevt); ++} ++ ++static int jh7110_timer_set_oneshot(struct clock_event_device *evt) ++{ ++ struct jh7110_clkevt *clkevt = to_jh7110_clkevt(evt); ++ ++ writel(JH7110_TIMER_MODE_SINGLE, clkevt->base + JH7110_TIMER_CTL); ++ writel(JH7110_TIMER_MAX_TICKS, clkevt->base + JH7110_TIMER_LOAD); ++ ++ return jh7110_timer_start(clkevt); ++} ++ ++static int jh7110_timer_set_next_event(unsigned long next, ++ struct clock_event_device *evt) ++{ ++ struct jh7110_clkevt *clkevt = to_jh7110_clkevt(evt); ++ ++ writel(JH7110_TIMER_MODE_SINGLE, clkevt->base + JH7110_TIMER_CTL); ++ writel(next, clkevt->base + JH7110_TIMER_LOAD); ++ ++ return jh7110_timer_start(clkevt); ++} ++ ++static void jh7110_set_clockevent(struct clock_event_device *evt) ++{ ++ evt->features = CLOCK_EVT_FEAT_PERIODIC | ++ CLOCK_EVT_FEAT_ONESHOT | ++ CLOCK_EVT_FEAT_DYNIRQ; ++ evt->set_state_shutdown = jh7110_timer_shutdown; ++ evt->set_state_periodic = jh7110_timer_set_periodic; ++ evt->set_state_oneshot = jh7110_timer_set_oneshot; ++ evt->set_state_oneshot_stopped = jh7110_timer_shutdown; ++ evt->tick_resume = jh7110_timer_tick_resume; ++ evt->set_next_event = jh7110_timer_set_next_event; ++ evt->suspend = jh7110_timer_suspend; ++ evt->resume = jh7110_timer_resume; ++ evt->rating = JH7110_CLOCKEVENT_RATING; ++} ++ ++static u64 jh7110_timer_clocksource_read(struct clocksource *cs) ++{ ++ struct jh7110_clkevt *clkevt = container_of(cs, struct jh7110_clkevt, cs); ++ ++ return (u64)readl(clkevt->base + JH7110_TIMER_VALUE); ++} ++ ++static int jh7110_clocksource_init(struct jh7110_clkevt *clkevt) ++{ ++ int ret; ++ ++ clkevt->cs.name = clkevt->name; ++ clkevt->cs.rating = JH7110_CLOCK_SOURCE_RATING; ++ clkevt->cs.read = jh7110_timer_clocksource_read; ++ clkevt->cs.mask = CLOCKSOURCE_MASK(JH7110_VALID_BITS); ++ clkevt->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS; ++ ++ ret = clocksource_register_hz(&clkevt->cs, clkevt->rate); ++ if (ret) ++ return ret; ++ ++ clkevt->cs_is_valid = true; /* clocksource register done */ ++ writel(JH7110_TIMER_MODE_CONTIN, clkevt->base + JH7110_TIMER_CTL); ++ writel(JH7110_TIMER_MAX_TICKS, clkevt->base + JH7110_TIMER_LOAD); ++ ++ return jh7110_timer_start(clkevt); ++} ++ ++static void jh7110_clockevents_register(struct jh7110_clkevt *clkevt) ++{ ++ clkevt->rate = clk_get_rate(clkevt->clk); ++ ++ jh7110_set_clockevent(&clkevt->evt); ++ clkevt->evt.name = clkevt->name; ++ clkevt->evt.cpumask = cpu_possible_mask; ++ ++ clockevents_config_and_register(&clkevt->evt, clkevt->rate, ++ JH7110_TIMER_MIN_TICKS, JH7110_TIMER_MAX_TICKS); ++} ++ ++static void jh7110_timer_release(void *data) ++{ ++ struct jh7110_timer_priv *priv = data; ++ int i; ++ ++ for (i = 0; i < JH7110_TIMER_CH_MAX; i++) { ++ /* Disable each channel of timer */ ++ if (priv->clkevt[i].base) ++ writel(JH7110_TIMER_DIS, priv->clkevt[i].base + JH7110_TIMER_ENABLE); ++ ++ /* Avoid no initialization in the loop of the probe */ ++ if (!IS_ERR_OR_NULL(priv->clkevt[i].rst)) ++ reset_control_assert(priv->clkevt[i].rst); ++ ++ if (priv->clkevt[i].cs_is_valid) ++ clocksource_unregister(&priv->clkevt[i].cs); ++ } ++ ++ reset_control_assert(priv->prst); ++} ++ ++static int jh7110_timer_probe(struct platform_device *pdev) ++{ ++ struct jh7110_timer_priv *priv; ++ struct jh7110_clkevt *clkevt; ++ char name[sizeof("chX")]; ++ int ch; ++ int ret; ++ void __iomem *base; ++ ++ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ base = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(base)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(base), ++ "failed to map registers\n"); ++ ++ priv->prst = devm_reset_control_get_exclusive(&pdev->dev, "apb"); ++ if (IS_ERR(priv->prst)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(priv->prst), ++ "failed to get apb reset\n"); ++ ++ priv->pclk = devm_clk_get_enabled(&pdev->dev, "apb"); ++ if (IS_ERR(priv->pclk)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(priv->pclk), ++ "failed to get & enable apb clock\n"); ++ ++ ret = reset_control_deassert(priv->prst); ++ if (ret) ++ return dev_err_probe(&pdev->dev, ret, "failed to deassert apb reset\n"); ++ ++ ret = devm_add_action_or_reset(&pdev->dev, jh7110_timer_release, priv); ++ if (ret) ++ return ret; ++ ++ for (ch = 0; ch < JH7110_TIMER_CH_MAX; ch++) { ++ clkevt = &priv->clkevt[ch]; ++ snprintf(name, sizeof(name), "ch%d", ch); ++ ++ clkevt->base = base + JH7110_TIMER_CH_BASE(ch); ++ /* Ensure timer is disabled */ ++ writel(JH7110_TIMER_DIS, clkevt->base + JH7110_TIMER_ENABLE); ++ ++ clkevt->rst = devm_reset_control_get_exclusive(&pdev->dev, name); ++ if (IS_ERR(clkevt->rst)) ++ return PTR_ERR(clkevt->rst); ++ ++ clkevt->clk = devm_clk_get_enabled(&pdev->dev, name); ++ if (IS_ERR(clkevt->clk)) ++ return PTR_ERR(clkevt->clk); ++ ++ ret = reset_control_deassert(clkevt->rst); ++ if (ret) ++ return ret; ++ ++ clkevt->evt.irq = platform_get_irq(pdev, ch); ++ if (clkevt->evt.irq < 0) ++ return clkevt->evt.irq; ++ ++ snprintf(clkevt->name, sizeof(clkevt->name), "jh7110-timer.ch%d", ch); ++ jh7110_clockevents_register(clkevt); ++ ++ ret = devm_request_irq(&pdev->dev, clkevt->evt.irq, jh7110_timer_interrupt, ++ IRQF_TIMER | IRQF_IRQPOLL, ++ clkevt->name, &clkevt->evt); ++ if (ret) ++ return ret; ++ ++ ret = jh7110_clocksource_init(clkevt); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static const struct of_device_id jh7110_timer_match[] = { ++ { .compatible = "starfive,jh7110-timer", }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, jh7110_timer_match); ++ ++static struct platform_driver jh7110_timer_driver = { ++ .probe = jh7110_timer_probe, ++ .driver = { ++ .name = "jh7110-timer", ++ .of_match_table = jh7110_timer_match, ++ }, ++}; ++module_platform_driver(jh7110_timer_driver); ++ ++MODULE_AUTHOR("Xingyu Wu "); ++MODULE_DESCRIPTION("StarFive JH7110 timer driver"); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/starfive/patches-6.6/0004-dt-bindings-mmc-starfive-Remove-properties-from-requ.patch b/target/linux/starfive/patches-6.6/0004-dt-bindings-mmc-starfive-Remove-properties-from-requ.patch new file mode 100644 index 0000000000..9c1e78b29b --- /dev/null +++ b/target/linux/starfive/patches-6.6/0004-dt-bindings-mmc-starfive-Remove-properties-from-requ.patch @@ -0,0 +1,34 @@ +From b513eb2cabee212ba1a23839f18c0026a21e653e Mon Sep 17 00:00:00 2001 +From: William Qiu +Date: Fri, 22 Sep 2023 14:28:32 +0800 +Subject: [PATCH 004/116] dt-bindings: mmc: starfive: Remove properties from + required + +Due to the change of tuning implementation, it's no longer necessary to +use the "starfive,sysreg" property in dts, so remove it from required. + +Signed-off-by: William Qiu +Acked-by: Conor Dooley +Reviewed-by: Emil Renner Berthing +Link: https://lore.kernel.org/r/20230922062834.39212-2-william.qiu@starfivetech.com +Signed-off-by: Ulf Hansson +--- + Documentation/devicetree/bindings/mmc/starfive,jh7110-mmc.yaml | 2 -- + 1 file changed, 2 deletions(-) + +--- a/Documentation/devicetree/bindings/mmc/starfive,jh7110-mmc.yaml ++++ b/Documentation/devicetree/bindings/mmc/starfive,jh7110-mmc.yaml +@@ -55,7 +55,6 @@ required: + - clocks + - clock-names + - interrupts +- - starfive,sysreg + + unevaluatedProperties: false + +@@ -73,5 +72,4 @@ examples: + fifo-depth = <32>; + fifo-watermark-aligned; + data-addr = <0>; +- starfive,sysreg = <&sys_syscon 0x14 0x1a 0x7c000000>; + }; diff --git a/target/linux/starfive/patches-6.6/0005-mmc-starfive-Change-tuning-implementation.patch b/target/linux/starfive/patches-6.6/0005-mmc-starfive-Change-tuning-implementation.patch new file mode 100644 index 0000000000..b8ea96f3c8 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0005-mmc-starfive-Change-tuning-implementation.patch @@ -0,0 +1,199 @@ +From 3ae8cec8fd28e18847edb67dfea04718c2f3369f Mon Sep 17 00:00:00 2001 +From: William Qiu +Date: Fri, 22 Sep 2023 14:28:33 +0800 +Subject: [PATCH 005/116] mmc: starfive: Change tuning implementation + +Before, we used syscon to achieve tuning, but the actual measurement +showed little effect, so the tuning implementation was modified here, +and it was realized by reading and writing the UHS_REG_EXT register. + +Signed-off-by: William Qiu +Reviewed-by: Emil Renner Berthing +Link: https://lore.kernel.org/r/20230922062834.39212-3-william.qiu@starfivetech.com +Signed-off-by: Ulf Hansson +--- + drivers/mmc/host/dw_mmc-starfive.c | 137 +++++++++-------------------- + 1 file changed, 40 insertions(+), 97 deletions(-) + +--- a/drivers/mmc/host/dw_mmc-starfive.c ++++ b/drivers/mmc/host/dw_mmc-starfive.c +@@ -5,6 +5,7 @@ + * Copyright (c) 2022 StarFive Technology Co., Ltd. + */ + ++#include + #include + #include + #include +@@ -20,13 +21,7 @@ + #define ALL_INT_CLR 0x1ffff + #define MAX_DELAY_CHAIN 32 + +-struct starfive_priv { +- struct device *dev; +- struct regmap *reg_syscon; +- u32 syscon_offset; +- u32 syscon_shift; +- u32 syscon_mask; +-}; ++#define STARFIVE_SMPL_PHASE GENMASK(20, 16) + + static void dw_mci_starfive_set_ios(struct dw_mci *host, struct mmc_ios *ios) + { +@@ -44,117 +39,65 @@ static void dw_mci_starfive_set_ios(stru + } + } + ++static void dw_mci_starfive_set_sample_phase(struct dw_mci *host, u32 smpl_phase) ++{ ++ /* change driver phase and sample phase */ ++ u32 reg_value = mci_readl(host, UHS_REG_EXT); ++ ++ /* In UHS_REG_EXT, only 5 bits valid in DRV_PHASE and SMPL_PHASE */ ++ reg_value &= ~STARFIVE_SMPL_PHASE; ++ reg_value |= FIELD_PREP(STARFIVE_SMPL_PHASE, smpl_phase); ++ mci_writel(host, UHS_REG_EXT, reg_value); ++ ++ /* We should delay 1ms wait for timing setting finished. */ ++ mdelay(1); ++} ++ + static int dw_mci_starfive_execute_tuning(struct dw_mci_slot *slot, + u32 opcode) + { + static const int grade = MAX_DELAY_CHAIN; + struct dw_mci *host = slot->host; +- struct starfive_priv *priv = host->priv; +- int rise_point = -1, fall_point = -1; +- int err, prev_err = 0; +- int i; +- bool found = 0; +- u32 regval; +- +- /* +- * Use grade as the max delay chain, and use the rise_point and +- * fall_point to ensure the best sampling point of a data input +- * signals. +- */ +- for (i = 0; i < grade; i++) { +- regval = i << priv->syscon_shift; +- err = regmap_update_bits(priv->reg_syscon, priv->syscon_offset, +- priv->syscon_mask, regval); +- if (err) +- return err; +- mci_writel(host, RINTSTS, ALL_INT_CLR); +- +- err = mmc_send_tuning(slot->mmc, opcode, NULL); +- if (!err) +- found = 1; +- +- if (i > 0) { +- if (err && !prev_err) +- fall_point = i - 1; +- if (!err && prev_err) +- rise_point = i; +- } ++ int smpl_phase, smpl_raise = -1, smpl_fall = -1; ++ int ret; + +- if (rise_point != -1 && fall_point != -1) +- goto tuning_out; ++ for (smpl_phase = 0; smpl_phase < grade; smpl_phase++) { ++ dw_mci_starfive_set_sample_phase(host, smpl_phase); ++ mci_writel(host, RINTSTS, ALL_INT_CLR); + +- prev_err = err; +- err = 0; +- } ++ ret = mmc_send_tuning(slot->mmc, opcode, NULL); + +-tuning_out: +- if (found) { +- if (rise_point == -1) +- rise_point = 0; +- if (fall_point == -1) +- fall_point = grade - 1; +- if (fall_point < rise_point) { +- if ((rise_point + fall_point) > +- (grade - 1)) +- i = fall_point / 2; +- else +- i = (rise_point + grade - 1) / 2; +- } else { +- i = (rise_point + fall_point) / 2; ++ if (!ret && smpl_raise < 0) { ++ smpl_raise = smpl_phase; ++ } else if (ret && smpl_raise >= 0) { ++ smpl_fall = smpl_phase - 1; ++ break; + } +- +- regval = i << priv->syscon_shift; +- err = regmap_update_bits(priv->reg_syscon, priv->syscon_offset, +- priv->syscon_mask, regval); +- if (err) +- return err; +- mci_writel(host, RINTSTS, ALL_INT_CLR); +- +- dev_info(host->dev, "Found valid delay chain! use it [delay=%d]\n", i); +- } else { +- dev_err(host->dev, "No valid delay chain! use default\n"); +- err = -EINVAL; + } + +- mci_writel(host, RINTSTS, ALL_INT_CLR); +- return err; +-} +- +-static int dw_mci_starfive_parse_dt(struct dw_mci *host) +-{ +- struct of_phandle_args args; +- struct starfive_priv *priv; +- int ret; ++ if (smpl_phase >= grade) ++ smpl_fall = grade - 1; + +- priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL); +- if (!priv) +- return -ENOMEM; +- +- ret = of_parse_phandle_with_fixed_args(host->dev->of_node, +- "starfive,sysreg", 3, 0, &args); +- if (ret) { +- dev_err(host->dev, "Failed to parse starfive,sysreg\n"); +- return -EINVAL; ++ if (smpl_raise < 0) { ++ smpl_phase = 0; ++ dev_err(host->dev, "No valid delay chain! use default\n"); ++ ret = -EINVAL; ++ goto out; + } + +- priv->reg_syscon = syscon_node_to_regmap(args.np); +- of_node_put(args.np); +- if (IS_ERR(priv->reg_syscon)) +- return PTR_ERR(priv->reg_syscon); +- +- priv->syscon_offset = args.args[0]; +- priv->syscon_shift = args.args[1]; +- priv->syscon_mask = args.args[2]; +- +- host->priv = priv; ++ smpl_phase = (smpl_raise + smpl_fall) / 2; ++ dev_dbg(host->dev, "Found valid delay chain! use it [delay=%d]\n", smpl_phase); ++ ret = 0; + +- return 0; ++out: ++ dw_mci_starfive_set_sample_phase(host, smpl_phase); ++ mci_writel(host, RINTSTS, ALL_INT_CLR); ++ return ret; + } + + static const struct dw_mci_drv_data starfive_data = { + .common_caps = MMC_CAP_CMD23, + .set_ios = dw_mci_starfive_set_ios, +- .parse_dt = dw_mci_starfive_parse_dt, + .execute_tuning = dw_mci_starfive_execute_tuning, + }; + diff --git a/target/linux/starfive/patches-6.6/0006-dt-bindings-pwm-Add-bindings-for-OpenCores-PWM-Contr.patch b/target/linux/starfive/patches-6.6/0006-dt-bindings-pwm-Add-bindings-for-OpenCores-PWM-Contr.patch new file mode 100644 index 0000000000..aafbee5d42 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0006-dt-bindings-pwm-Add-bindings-for-OpenCores-PWM-Contr.patch @@ -0,0 +1,79 @@ +From e366df2ff64e9f93a5b35eea6a198b005d5a0911 Mon Sep 17 00:00:00 2001 +From: William Qiu +Date: Fri, 22 Dec 2023 17:45:45 +0800 +Subject: [PATCH 006/116] dt-bindings: pwm: Add bindings for OpenCores PWM + Controller +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add bindings for OpenCores PWM Controller. + +Signed-off-by: William Qiu +Reviewed-by: Hal Feng +Reviewed-by: Conor Dooley +Reviewed-by: Uwe Kleine-König +Acked-by: Uwe Kleine-König +--- + .../bindings/pwm/opencores,pwm.yaml | 55 +++++++++++++++++++ + 1 file changed, 55 insertions(+) + create mode 100644 Documentation/devicetree/bindings/pwm/opencores,pwm.yaml + +--- /dev/null ++++ b/Documentation/devicetree/bindings/pwm/opencores,pwm.yaml +@@ -0,0 +1,55 @@ ++# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/pwm/opencores,pwm.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: OpenCores PWM controller ++ ++maintainers: ++ - William Qiu ++ ++description: ++ The OpenCores PTC ip core contains a PWM controller. When operating in PWM ++ mode, the PTC core generates binary signal with user-programmable low and ++ high periods. All PTC counters and registers are 32-bit. ++ ++allOf: ++ - $ref: pwm.yaml# ++ ++properties: ++ compatible: ++ items: ++ - enum: ++ - starfive,jh7100-pwm ++ - starfive,jh7110-pwm ++ - const: opencores,pwm-v1 ++ ++ reg: ++ maxItems: 1 ++ ++ clocks: ++ maxItems: 1 ++ ++ resets: ++ maxItems: 1 ++ ++ "#pwm-cells": ++ const: 3 ++ ++required: ++ - compatible ++ - reg ++ - clocks ++ ++additionalProperties: false ++ ++examples: ++ - | ++ pwm@12490000 { ++ compatible = "starfive,jh7110-pwm", "opencores,pwm-v1"; ++ reg = <0x12490000 0x10000>; ++ clocks = <&clkgen 181>; ++ resets = <&rstgen 109>; ++ #pwm-cells = <3>; ++ }; diff --git a/target/linux/starfive/patches-6.6/0007-pwm-opencores-Add-PWM-driver-support.patch b/target/linux/starfive/patches-6.6/0007-pwm-opencores-Add-PWM-driver-support.patch new file mode 100644 index 0000000000..27e510d86a --- /dev/null +++ b/target/linux/starfive/patches-6.6/0007-pwm-opencores-Add-PWM-driver-support.patch @@ -0,0 +1,285 @@ +From 644bfe581dde9b762460a4916da4d71c148be06e Mon Sep 17 00:00:00 2001 +From: William Qiu +Date: Fri, 22 Dec 2023 17:45:46 +0800 +Subject: [PATCH 007/116] pwm: opencores: Add PWM driver support + +Add driver for OpenCores PWM Controller. And add compatibility code +which based on StarFive SoC. + +Co-developed-by: Hal Feng +Signed-off-by: Hal Feng +Signed-off-by: William Qiu +--- + drivers/pwm/Kconfig | 12 ++ + drivers/pwm/Makefile | 1 + + drivers/pwm/pwm-ocores.c | 233 +++++++++++++++++++++++++++++++++++++++ + 3 files changed, 246 insertions(+) + create mode 100644 drivers/pwm/pwm-ocores.c + +--- a/drivers/pwm/Kconfig ++++ b/drivers/pwm/Kconfig +@@ -434,6 +434,18 @@ config PWM_NTXEC + controller found in certain e-book readers designed by the original + design manufacturer Netronix. + ++config PWM_OCORES ++ tristate "OpenCores PWM support" ++ depends on HAS_IOMEM && OF ++ depends on COMMON_CLK ++ depends on ARCH_STARFIVE || COMPILE_TEST ++ help ++ If you say yes to this option, support will be included for the ++ OpenCores PWM. For details see https://opencores.org/projects/ptc. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called pwm-ocores. ++ + config PWM_OMAP_DMTIMER + tristate "OMAP Dual-Mode Timer PWM support" + depends on OF +--- a/drivers/pwm/Makefile ++++ b/drivers/pwm/Makefile +@@ -39,6 +39,7 @@ obj-$(CONFIG_PWM_MICROCHIP_CORE) += pwm- + obj-$(CONFIG_PWM_MTK_DISP) += pwm-mtk-disp.o + obj-$(CONFIG_PWM_MXS) += pwm-mxs.o + obj-$(CONFIG_PWM_NTXEC) += pwm-ntxec.o ++obj-$(CONFIG_PWM_OCORES) += pwm-ocores.o + obj-$(CONFIG_PWM_OMAP_DMTIMER) += pwm-omap-dmtimer.o + obj-$(CONFIG_PWM_PCA9685) += pwm-pca9685.o + obj-$(CONFIG_PWM_PXA) += pwm-pxa.o +--- /dev/null ++++ b/drivers/pwm/pwm-ocores.c +@@ -0,0 +1,233 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * OpenCores PWM Driver ++ * ++ * https://opencores.org/projects/ptc ++ * ++ * Copyright (C) 2018-2023 StarFive Technology Co., Ltd. ++ * ++ * Limitations: ++ * - The hardware only do inverted polarity. ++ * - The hardware minimum period / duty_cycle is (1 / pwm_apb clock frequency) ns. ++ * - The hardware maximum period / duty_cycle is (U32_MAX / pwm_apb clock frequency) ns. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* OCPWM_CTRL register bits*/ ++#define REG_OCPWM_EN BIT(0) ++#define REG_OCPWM_ECLK BIT(1) ++#define REG_OCPWM_NEC BIT(2) ++#define REG_OCPWM_OE BIT(3) ++#define REG_OCPWM_SIGNLE BIT(4) ++#define REG_OCPWM_INTE BIT(5) ++#define REG_OCPWM_INT BIT(6) ++#define REG_OCPWM_CNTRRST BIT(7) ++#define REG_OCPWM_CAPTE BIT(8) ++ ++struct ocores_pwm_device { ++ struct pwm_chip chip; ++ struct clk *clk; ++ struct reset_control *rst; ++ const struct ocores_pwm_data *data; ++ void __iomem *regs; ++ u32 clk_rate; /* PWM APB clock frequency */ ++}; ++ ++struct ocores_pwm_data { ++ void __iomem *(*get_ch_base)(void __iomem *base, unsigned int channel); ++}; ++ ++static inline u32 ocores_readl(struct ocores_pwm_device *ddata, ++ unsigned int channel, ++ unsigned int offset) ++{ ++ void __iomem *base = ddata->data->get_ch_base ? ++ ddata->data->get_ch_base(ddata->regs, channel) : ddata->regs; ++ ++ return readl(base + offset); ++} ++ ++static inline void ocores_writel(struct ocores_pwm_device *ddata, ++ unsigned int channel, ++ unsigned int offset, u32 val) ++{ ++ void __iomem *base = ddata->data->get_ch_base ? ++ ddata->data->get_ch_base(ddata->regs, channel) : ddata->regs; ++ ++ writel(val, base + offset); ++} ++ ++static inline struct ocores_pwm_device *chip_to_ocores(struct pwm_chip *chip) ++{ ++ return container_of(chip, struct ocores_pwm_device, chip); ++} ++ ++static void __iomem *starfive_jh71x0_get_ch_base(void __iomem *base, ++ unsigned int channel) ++{ ++ unsigned int offset = (channel > 3 ? 1 << 15 : 0) + (channel & 3) * 0x10; ++ ++ return base + offset; ++} ++ ++static int ocores_pwm_get_state(struct pwm_chip *chip, ++ struct pwm_device *pwm, ++ struct pwm_state *state) ++{ ++ struct ocores_pwm_device *ddata = chip_to_ocores(chip); ++ u32 period_data, duty_data, ctrl_data; ++ ++ period_data = ocores_readl(ddata, pwm->hwpwm, 0x8); ++ duty_data = ocores_readl(ddata, pwm->hwpwm, 0x4); ++ ctrl_data = ocores_readl(ddata, pwm->hwpwm, 0xC); ++ ++ state->period = DIV_ROUND_UP_ULL((u64)period_data * NSEC_PER_SEC, ddata->clk_rate); ++ state->duty_cycle = DIV_ROUND_UP_ULL((u64)duty_data * NSEC_PER_SEC, ddata->clk_rate); ++ state->polarity = PWM_POLARITY_INVERSED; ++ state->enabled = (ctrl_data & REG_OCPWM_EN) ? true : false; ++ ++ return 0; ++} ++ ++static int ocores_pwm_apply(struct pwm_chip *chip, ++ struct pwm_device *pwm, ++ const struct pwm_state *state) ++{ ++ struct ocores_pwm_device *ddata = chip_to_ocores(chip); ++ u32 ctrl_data = 0; ++ u64 period_data, duty_data; ++ ++ if (state->polarity != PWM_POLARITY_INVERSED) ++ return -EINVAL; ++ ++ ctrl_data = ocores_readl(ddata, pwm->hwpwm, 0xC); ++ ocores_writel(ddata, pwm->hwpwm, 0xC, 0); ++ ++ period_data = DIV_ROUND_DOWN_ULL(state->period * ddata->clk_rate, NSEC_PER_SEC); ++ if (period_data <= U32_MAX) ++ ocores_writel(ddata, pwm->hwpwm, 0x8, (u32)period_data); ++ else ++ return -EINVAL; ++ ++ duty_data = DIV_ROUND_DOWN_ULL(state->duty_cycle * ddata->clk_rate, NSEC_PER_SEC); ++ if (duty_data <= U32_MAX) ++ ocores_writel(ddata, pwm->hwpwm, 0x4, (u32)duty_data); ++ else ++ return -EINVAL; ++ ++ ocores_writel(ddata, pwm->hwpwm, 0xC, 0); ++ ++ if (state->enabled) { ++ ctrl_data = ocores_readl(ddata, pwm->hwpwm, 0xC); ++ ocores_writel(ddata, pwm->hwpwm, 0xC, ctrl_data | REG_OCPWM_EN | REG_OCPWM_OE); ++ } ++ ++ return 0; ++} ++ ++static const struct pwm_ops ocores_pwm_ops = { ++ .get_state = ocores_pwm_get_state, ++ .apply = ocores_pwm_apply, ++}; ++ ++static const struct ocores_pwm_data jh7100_pwm_data = { ++ .get_ch_base = starfive_jh71x0_get_ch_base, ++}; ++ ++static const struct ocores_pwm_data jh7110_pwm_data = { ++ .get_ch_base = starfive_jh71x0_get_ch_base, ++}; ++ ++static const struct of_device_id ocores_pwm_of_match[] = { ++ { .compatible = "opencores,pwm-v1" }, ++ { .compatible = "starfive,jh7100-pwm", .data = &jh7100_pwm_data}, ++ { .compatible = "starfive,jh7110-pwm", .data = &jh7110_pwm_data}, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, ocores_pwm_of_match); ++ ++static void ocores_reset_control_assert(void *data) ++{ ++ reset_control_assert(data); ++} ++ ++static int ocores_pwm_probe(struct platform_device *pdev) ++{ ++ const struct of_device_id *id; ++ struct device *dev = &pdev->dev; ++ struct ocores_pwm_device *ddata; ++ struct pwm_chip *chip; ++ int ret; ++ ++ id = of_match_device(ocores_pwm_of_match, dev); ++ if (!id) ++ return -EINVAL; ++ ++ ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL); ++ if (!ddata) ++ return -ENOMEM; ++ ++ ddata->data = id->data; ++ chip = &ddata->chip; ++ chip->dev = dev; ++ chip->ops = &ocores_pwm_ops; ++ chip->npwm = 8; ++ chip->of_pwm_n_cells = 3; ++ ++ ddata->regs = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(ddata->regs)) ++ return dev_err_probe(dev, PTR_ERR(ddata->regs), ++ "Unable to map IO resources\n"); ++ ++ ddata->clk = devm_clk_get_enabled(dev, NULL); ++ if (IS_ERR(ddata->clk)) ++ return dev_err_probe(dev, PTR_ERR(ddata->clk), ++ "Unable to get pwm's clock\n"); ++ ++ ddata->rst = devm_reset_control_get_optional_exclusive(dev, NULL); ++ if (IS_ERR(ddata->rst)) ++ return dev_err_probe(dev, PTR_ERR(ddata->rst), ++ "Unable to get pwm's reset\n"); ++ ++ reset_control_deassert(ddata->rst); ++ ++ ret = devm_add_action_or_reset(dev, ocores_reset_control_assert, ddata->rst); ++ if (ret) ++ return ret; ++ ++ ddata->clk_rate = clk_get_rate(ddata->clk); ++ if (ddata->clk_rate <= 0) ++ return dev_err_probe(dev, ddata->clk_rate, ++ "Unable to get clock's rate\n"); ++ ++ ret = devm_pwmchip_add(dev, chip); ++ if (ret < 0) ++ return dev_err_probe(dev, ret, "Could not register PWM chip\n"); ++ ++ platform_set_drvdata(pdev, ddata); ++ ++ return ret; ++} ++ ++static struct platform_driver ocores_pwm_driver = { ++ .probe = ocores_pwm_probe, ++ .driver = { ++ .name = "ocores-pwm", ++ .of_match_table = ocores_pwm_of_match, ++ }, ++}; ++module_platform_driver(ocores_pwm_driver); ++ ++MODULE_AUTHOR("Jieqin Chen"); ++MODULE_AUTHOR("Hal Feng "); ++MODULE_DESCRIPTION("OpenCores PWM PTC driver"); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/starfive/patches-6.6/0008-crypto-starfive-remove-unnecessary-alignmask-for-aha.patch b/target/linux/starfive/patches-6.6/0008-crypto-starfive-remove-unnecessary-alignmask-for-aha.patch new file mode 100644 index 0000000000..a6ede31342 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0008-crypto-starfive-remove-unnecessary-alignmask-for-aha.patch @@ -0,0 +1,117 @@ +From 7d9521cad6474d45e9056176982e6da54d40bc19 Mon Sep 17 00:00:00 2001 +From: Eric Biggers +Date: Sun, 22 Oct 2023 01:10:42 -0700 +Subject: [PATCH 008/116] crypto: starfive - remove unnecessary alignmask for + ahashes + +The crypto API's support for alignmasks for ahash algorithms is nearly +useless, as its only effect is to cause the API to align the key and +result buffers. The drivers that happen to be specifying an alignmask +for ahash rarely actually need it. When they do, it's easily fixable, +especially considering that these buffers cannot be used for DMA. + +In preparation for removing alignmask support from ahash, this patch +makes the starfive driver no longer use it. This driver did actually +rely on it, but only for storing to the result buffer using int stores +in starfive_hash_copy_hash(). This patch makes +starfive_hash_copy_hash() use put_unaligned() instead. (It really +should use a specific endianness, but that's an existing bug.) + +Signed-off-by: Eric Biggers +Signed-off-by: Herbert Xu +--- + drivers/crypto/starfive/jh7110-hash.c | 13 ++----------- + 1 file changed, 2 insertions(+), 11 deletions(-) + +--- a/drivers/crypto/starfive/jh7110-hash.c ++++ b/drivers/crypto/starfive/jh7110-hash.c +@@ -209,7 +209,8 @@ static int starfive_hash_copy_hash(struc + data = (u32 *)req->result; + + for (count = 0; count < mlen; count++) +- data[count] = readl(ctx->cryp->base + STARFIVE_HASH_SHARDR); ++ put_unaligned(readl(ctx->cryp->base + STARFIVE_HASH_SHARDR), ++ &data[count]); + + return 0; + } +@@ -628,7 +629,6 @@ static struct ahash_engine_alg algs_sha2 + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SHA224_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct starfive_cryp_ctx), +- .cra_alignmask = 3, + .cra_module = THIS_MODULE, + } + }, +@@ -658,7 +658,6 @@ static struct ahash_engine_alg algs_sha2 + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SHA224_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct starfive_cryp_ctx), +- .cra_alignmask = 3, + .cra_module = THIS_MODULE, + } + }, +@@ -687,7 +686,6 @@ static struct ahash_engine_alg algs_sha2 + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SHA256_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct starfive_cryp_ctx), +- .cra_alignmask = 3, + .cra_module = THIS_MODULE, + } + }, +@@ -717,7 +715,6 @@ static struct ahash_engine_alg algs_sha2 + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SHA256_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct starfive_cryp_ctx), +- .cra_alignmask = 3, + .cra_module = THIS_MODULE, + } + }, +@@ -746,7 +743,6 @@ static struct ahash_engine_alg algs_sha2 + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SHA384_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct starfive_cryp_ctx), +- .cra_alignmask = 3, + .cra_module = THIS_MODULE, + } + }, +@@ -776,7 +772,6 @@ static struct ahash_engine_alg algs_sha2 + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SHA384_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct starfive_cryp_ctx), +- .cra_alignmask = 3, + .cra_module = THIS_MODULE, + } + }, +@@ -805,7 +800,6 @@ static struct ahash_engine_alg algs_sha2 + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SHA512_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct starfive_cryp_ctx), +- .cra_alignmask = 3, + .cra_module = THIS_MODULE, + } + }, +@@ -835,7 +829,6 @@ static struct ahash_engine_alg algs_sha2 + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SHA512_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct starfive_cryp_ctx), +- .cra_alignmask = 3, + .cra_module = THIS_MODULE, + } + }, +@@ -864,7 +857,6 @@ static struct ahash_engine_alg algs_sha2 + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SM3_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct starfive_cryp_ctx), +- .cra_alignmask = 3, + .cra_module = THIS_MODULE, + } + }, +@@ -894,7 +886,6 @@ static struct ahash_engine_alg algs_sha2 + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SM3_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct starfive_cryp_ctx), +- .cra_alignmask = 3, + .cra_module = THIS_MODULE, + } + }, diff --git a/target/linux/starfive/patches-6.6/0009-crypto-starfive-Update-driver-dependencies.patch b/target/linux/starfive/patches-6.6/0009-crypto-starfive-Update-driver-dependencies.patch new file mode 100644 index 0000000000..145c01377d --- /dev/null +++ b/target/linux/starfive/patches-6.6/0009-crypto-starfive-Update-driver-dependencies.patch @@ -0,0 +1,25 @@ +From 52de0270ed6453727936b5a793dc367d75280e84 Mon Sep 17 00:00:00 2001 +From: Jia Jie Ho +Date: Wed, 15 Nov 2023 01:12:13 +0800 +Subject: [PATCH 009/116] crypto: starfive - Update driver dependencies + +Change AMBA_PL08X to required dependency as the hash ops depends on it +for data transfer. + +Signed-off-by: Jia Jie Ho +Signed-off-by: Herbert Xu +--- + drivers/crypto/starfive/Kconfig | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/crypto/starfive/Kconfig ++++ b/drivers/crypto/starfive/Kconfig +@@ -4,7 +4,7 @@ + + config CRYPTO_DEV_JH7110 + tristate "StarFive JH7110 cryptographic engine driver" +- depends on SOC_STARFIVE || AMBA_PL08X || COMPILE_TEST ++ depends on (SOC_STARFIVE && AMBA_PL08X) || COMPILE_TEST + depends on HAS_DMA + select CRYPTO_ENGINE + select CRYPTO_HMAC diff --git a/target/linux/starfive/patches-6.6/0010-crypto-starfive-RSA-poll-csr-for-done-status.patch b/target/linux/starfive/patches-6.6/0010-crypto-starfive-RSA-poll-csr-for-done-status.patch new file mode 100644 index 0000000000..c94009d4ff --- /dev/null +++ b/target/linux/starfive/patches-6.6/0010-crypto-starfive-RSA-poll-csr-for-done-status.patch @@ -0,0 +1,200 @@ +From 68a1bbb99455fd5ea80b7e21ec726f369abc9572 Mon Sep 17 00:00:00 2001 +From: Jia Jie Ho +Date: Wed, 15 Nov 2023 01:12:14 +0800 +Subject: [PATCH 010/116] crypto: starfive - RSA poll csr for done status + +Hardware could not clear irq status without resetting the entire module. +Driver receives irq immediately when mask bit is cleared causing +intermittent errors in RSA calculations. Switch to use csr polling for +done status instead. + +Signed-off-by: Jia Jie Ho +Signed-off-by: Herbert Xu +--- + drivers/crypto/starfive/jh7110-cryp.c | 8 ----- + drivers/crypto/starfive/jh7110-cryp.h | 10 +++++- + drivers/crypto/starfive/jh7110-rsa.c | 49 +++++++-------------------- + 3 files changed, 22 insertions(+), 45 deletions(-) + +--- a/drivers/crypto/starfive/jh7110-cryp.c ++++ b/drivers/crypto/starfive/jh7110-cryp.c +@@ -109,12 +109,6 @@ static irqreturn_t starfive_cryp_irq(int + tasklet_schedule(&cryp->hash_done); + } + +- if (status & STARFIVE_IE_FLAG_PKA_DONE) { +- mask |= STARFIVE_IE_MASK_PKA_DONE; +- writel(mask, cryp->base + STARFIVE_IE_MASK_OFFSET); +- complete(&cryp->pka_done); +- } +- + return IRQ_HANDLED; + } + +@@ -159,8 +153,6 @@ static int starfive_cryp_probe(struct pl + return dev_err_probe(&pdev->dev, PTR_ERR(cryp->rst), + "Error getting hardware reset line\n"); + +- init_completion(&cryp->pka_done); +- + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; +--- a/drivers/crypto/starfive/jh7110-cryp.h ++++ b/drivers/crypto/starfive/jh7110-cryp.h +@@ -125,6 +125,15 @@ union starfive_pka_cacr { + }; + }; + ++union starfive_pka_casr { ++ u32 v; ++ struct { ++#define STARFIVE_PKA_DONE BIT(0) ++ u32 done :1; ++ u32 rsvd_0 :31; ++ }; ++}; ++ + struct starfive_rsa_key { + u8 *n; + u8 *e; +@@ -183,7 +192,6 @@ struct starfive_cryp_dev { + struct crypto_engine *engine; + struct tasklet_struct aes_done; + struct tasklet_struct hash_done; +- struct completion pka_done; + size_t assoclen; + size_t total_in; + size_t total_out; +--- a/drivers/crypto/starfive/jh7110-rsa.c ++++ b/drivers/crypto/starfive/jh7110-rsa.c +@@ -6,13 +6,7 @@ + */ + + #include +-#include +-#include +-#include +-#include + #include +-#include +-#include + #include + #include + #include +@@ -28,13 +22,13 @@ + #define STARFIVE_PKA_CAER_OFFSET (STARFIVE_PKA_REGS_OFFSET + 0x108) + #define STARFIVE_PKA_CANR_OFFSET (STARFIVE_PKA_REGS_OFFSET + 0x208) + +-// R^2 mod N and N0' ++/* R ^ 2 mod N and N0' */ + #define CRYPTO_CMD_PRE 0x0 +-// A * R mod N ==> A ++/* A * R mod N ==> A */ + #define CRYPTO_CMD_ARN 0x5 +-// A * E * R mod N ==> A ++/* A * E * R mod N ==> A */ + #define CRYPTO_CMD_AERN 0x6 +-// A * A * R mod N ==> A ++/* A * A * R mod N ==> A */ + #define CRYPTO_CMD_AARN 0x7 + + #define STARFIVE_RSA_MAX_KEYSZ 256 +@@ -43,21 +37,10 @@ + static inline int starfive_pka_wait_done(struct starfive_cryp_ctx *ctx) + { + struct starfive_cryp_dev *cryp = ctx->cryp; ++ u32 status; + +- return wait_for_completion_timeout(&cryp->pka_done, +- usecs_to_jiffies(100000)); +-} +- +-static inline void starfive_pka_irq_mask_clear(struct starfive_cryp_ctx *ctx) +-{ +- struct starfive_cryp_dev *cryp = ctx->cryp; +- u32 stat; +- +- stat = readl(cryp->base + STARFIVE_IE_MASK_OFFSET); +- stat &= ~STARFIVE_IE_MASK_PKA_DONE; +- writel(stat, cryp->base + STARFIVE_IE_MASK_OFFSET); +- +- reinit_completion(&cryp->pka_done); ++ return readl_relaxed_poll_timeout(cryp->base + STARFIVE_PKA_CASR_OFFSET, status, ++ status & STARFIVE_PKA_DONE, 10, 100000); + } + + static void starfive_rsa_free_key(struct starfive_rsa_key *key) +@@ -114,10 +97,9 @@ static int starfive_rsa_montgomery_form( + rctx->csr.pka.not_r2 = 1; + rctx->csr.pka.ie = 1; + +- starfive_pka_irq_mask_clear(ctx); + writel(rctx->csr.pka.v, cryp->base + STARFIVE_PKA_CACR_OFFSET); + +- if (!starfive_pka_wait_done(ctx)) ++ if (starfive_pka_wait_done(ctx)) + return -ETIMEDOUT; + + for (loop = 0; loop <= opsize; loop++) +@@ -136,10 +118,9 @@ static int starfive_rsa_montgomery_form( + rctx->csr.pka.start = 1; + rctx->csr.pka.ie = 1; + +- starfive_pka_irq_mask_clear(ctx); + writel(rctx->csr.pka.v, cryp->base + STARFIVE_PKA_CACR_OFFSET); + +- if (!starfive_pka_wait_done(ctx)) ++ if (starfive_pka_wait_done(ctx)) + return -ETIMEDOUT; + } else { + rctx->csr.pka.v = 0; +@@ -151,10 +132,9 @@ static int starfive_rsa_montgomery_form( + rctx->csr.pka.pre_expf = 1; + rctx->csr.pka.ie = 1; + +- starfive_pka_irq_mask_clear(ctx); + writel(rctx->csr.pka.v, cryp->base + STARFIVE_PKA_CACR_OFFSET); + +- if (!starfive_pka_wait_done(ctx)) ++ if (starfive_pka_wait_done(ctx)) + return -ETIMEDOUT; + + for (loop = 0; loop <= count; loop++) +@@ -172,10 +152,9 @@ static int starfive_rsa_montgomery_form( + rctx->csr.pka.start = 1; + rctx->csr.pka.ie = 1; + +- starfive_pka_irq_mask_clear(ctx); + writel(rctx->csr.pka.v, cryp->base + STARFIVE_PKA_CACR_OFFSET); + +- if (!starfive_pka_wait_done(ctx)) ++ if (starfive_pka_wait_done(ctx)) + return -ETIMEDOUT; + } + +@@ -226,11 +205,10 @@ static int starfive_rsa_cpu_start(struct + rctx->csr.pka.start = 1; + rctx->csr.pka.ie = 1; + +- starfive_pka_irq_mask_clear(ctx); + writel(rctx->csr.pka.v, cryp->base + STARFIVE_PKA_CACR_OFFSET); + + ret = -ETIMEDOUT; +- if (!starfive_pka_wait_done(ctx)) ++ if (starfive_pka_wait_done(ctx)) + goto rsa_err; + + if (mlen) { +@@ -242,10 +220,9 @@ static int starfive_rsa_cpu_start(struct + rctx->csr.pka.start = 1; + rctx->csr.pka.ie = 1; + +- starfive_pka_irq_mask_clear(ctx); + writel(rctx->csr.pka.v, cryp->base + STARFIVE_PKA_CACR_OFFSET); + +- if (!starfive_pka_wait_done(ctx)) ++ if (starfive_pka_wait_done(ctx)) + goto rsa_err; + } + } diff --git a/target/linux/starfive/patches-6.6/0011-crypto-starfive-Pad-adata-with-zeroes.patch b/target/linux/starfive/patches-6.6/0011-crypto-starfive-Pad-adata-with-zeroes.patch new file mode 100644 index 0000000000..268e4055e0 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0011-crypto-starfive-Pad-adata-with-zeroes.patch @@ -0,0 +1,46 @@ +From eea9f2c55cf944bbd5cdd43eb655416a867846af Mon Sep 17 00:00:00 2001 +From: Jia Jie Ho +Date: Mon, 20 Nov 2023 11:12:42 +0800 +Subject: [PATCH 011/116] crypto: starfive - Pad adata with zeroes + +Aad requires padding with zeroes up to 15 bytes in some cases. This +patch increases the allocated buffer size for aad and prevents the +driver accessing uninitialized memory region. + +v1->v2: Specify reason for alloc size change in descriptions. + +Signed-off-by: Jia Jie Ho +Signed-off-by: Herbert Xu +--- + drivers/crypto/starfive/jh7110-aes.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +--- a/drivers/crypto/starfive/jh7110-aes.c ++++ b/drivers/crypto/starfive/jh7110-aes.c +@@ -500,7 +500,7 @@ static int starfive_aes_prepare_req(stru + scatterwalk_start(&cryp->out_walk, rctx->out_sg); + + if (cryp->assoclen) { +- rctx->adata = kzalloc(ALIGN(cryp->assoclen, AES_BLOCK_SIZE), GFP_KERNEL); ++ rctx->adata = kzalloc(cryp->assoclen + AES_BLOCK_SIZE, GFP_KERNEL); + if (!rctx->adata) + return dev_err_probe(cryp->dev, -ENOMEM, + "Failed to alloc memory for adata"); +@@ -569,7 +569,7 @@ static int starfive_aes_aead_do_one_req( + struct starfive_cryp_ctx *ctx = + crypto_aead_ctx(crypto_aead_reqtfm(req)); + struct starfive_cryp_dev *cryp = ctx->cryp; +- struct starfive_cryp_request_ctx *rctx = ctx->rctx; ++ struct starfive_cryp_request_ctx *rctx; + u32 block[AES_BLOCK_32]; + u32 stat; + int err; +@@ -579,6 +579,8 @@ static int starfive_aes_aead_do_one_req( + if (err) + return err; + ++ rctx = ctx->rctx; ++ + if (!cryp->assoclen) + goto write_text; + diff --git a/target/linux/starfive/patches-6.6/0012-crypto-starfive-Remove-cfb-and-ofb.patch b/target/linux/starfive/patches-6.6/0012-crypto-starfive-Remove-cfb-and-ofb.patch new file mode 100644 index 0000000000..f376edb60e --- /dev/null +++ b/target/linux/starfive/patches-6.6/0012-crypto-starfive-Remove-cfb-and-ofb.patch @@ -0,0 +1,125 @@ +From 922b213ad22f93fb2788ce119084622ab3d25bf8 Mon Sep 17 00:00:00 2001 +From: Herbert Xu +Date: Thu, 30 Nov 2023 18:12:55 +0800 +Subject: [PATCH 012/116] crypto: starfive - Remove cfb and ofb + +Remove the unused CFB/OFB implementation. + +Signed-off-by: Herbert Xu +--- + drivers/crypto/starfive/jh7110-aes.c | 71 +-------------------------- + drivers/crypto/starfive/jh7110-cryp.h | 2 - + 2 files changed, 1 insertion(+), 72 deletions(-) + +--- a/drivers/crypto/starfive/jh7110-aes.c ++++ b/drivers/crypto/starfive/jh7110-aes.c +@@ -262,12 +262,7 @@ static int starfive_aes_hw_init(struct s + rctx->csr.aes.mode = hw_mode; + rctx->csr.aes.cmode = !is_encrypt(cryp); + rctx->csr.aes.ie = 1; +- +- if (hw_mode == STARFIVE_AES_MODE_CFB || +- hw_mode == STARFIVE_AES_MODE_OFB) +- rctx->csr.aes.stmode = STARFIVE_AES_MODE_XFB_128; +- else +- rctx->csr.aes.stmode = STARFIVE_AES_MODE_XFB_1; ++ rctx->csr.aes.stmode = STARFIVE_AES_MODE_XFB_1; + + if (cryp->side_chan) { + rctx->csr.aes.delay_aes = 1; +@@ -294,8 +289,6 @@ static int starfive_aes_hw_init(struct s + starfive_aes_ccm_init(ctx); + starfive_aes_aead_hw_start(ctx, hw_mode); + break; +- case STARFIVE_AES_MODE_OFB: +- case STARFIVE_AES_MODE_CFB: + case STARFIVE_AES_MODE_CBC: + case STARFIVE_AES_MODE_CTR: + starfive_aes_write_iv(ctx, (void *)cryp->req.sreq->iv); +@@ -785,26 +778,6 @@ static int starfive_aes_cbc_decrypt(stru + return starfive_aes_crypt(req, STARFIVE_AES_MODE_CBC); + } + +-static int starfive_aes_cfb_encrypt(struct skcipher_request *req) +-{ +- return starfive_aes_crypt(req, STARFIVE_AES_MODE_CFB | FLG_ENCRYPT); +-} +- +-static int starfive_aes_cfb_decrypt(struct skcipher_request *req) +-{ +- return starfive_aes_crypt(req, STARFIVE_AES_MODE_CFB); +-} +- +-static int starfive_aes_ofb_encrypt(struct skcipher_request *req) +-{ +- return starfive_aes_crypt(req, STARFIVE_AES_MODE_OFB | FLG_ENCRYPT); +-} +- +-static int starfive_aes_ofb_decrypt(struct skcipher_request *req) +-{ +- return starfive_aes_crypt(req, STARFIVE_AES_MODE_OFB); +-} +- + static int starfive_aes_ctr_encrypt(struct skcipher_request *req) + { + return starfive_aes_crypt(req, STARFIVE_AES_MODE_CTR | FLG_ENCRYPT); +@@ -903,48 +876,6 @@ static struct skcipher_engine_alg skciph + .cra_priority = 200, + .cra_flags = CRYPTO_ALG_ASYNC, + .cra_blocksize = 1, +- .cra_ctxsize = sizeof(struct starfive_cryp_ctx), +- .cra_alignmask = 0xf, +- .cra_module = THIS_MODULE, +- }, +- .op = { +- .do_one_request = starfive_aes_do_one_req, +- }, +-}, { +- .base.init = starfive_aes_init_tfm, +- .base.setkey = starfive_aes_setkey, +- .base.encrypt = starfive_aes_cfb_encrypt, +- .base.decrypt = starfive_aes_cfb_decrypt, +- .base.min_keysize = AES_MIN_KEY_SIZE, +- .base.max_keysize = AES_MAX_KEY_SIZE, +- .base.ivsize = AES_BLOCK_SIZE, +- .base.base = { +- .cra_name = "cfb(aes)", +- .cra_driver_name = "starfive-cfb-aes", +- .cra_priority = 200, +- .cra_flags = CRYPTO_ALG_ASYNC, +- .cra_blocksize = 1, +- .cra_ctxsize = sizeof(struct starfive_cryp_ctx), +- .cra_alignmask = 0xf, +- .cra_module = THIS_MODULE, +- }, +- .op = { +- .do_one_request = starfive_aes_do_one_req, +- }, +-}, { +- .base.init = starfive_aes_init_tfm, +- .base.setkey = starfive_aes_setkey, +- .base.encrypt = starfive_aes_ofb_encrypt, +- .base.decrypt = starfive_aes_ofb_decrypt, +- .base.min_keysize = AES_MIN_KEY_SIZE, +- .base.max_keysize = AES_MAX_KEY_SIZE, +- .base.ivsize = AES_BLOCK_SIZE, +- .base.base = { +- .cra_name = "ofb(aes)", +- .cra_driver_name = "starfive-ofb-aes", +- .cra_priority = 200, +- .cra_flags = CRYPTO_ALG_ASYNC, +- .cra_blocksize = 1, + .cra_ctxsize = sizeof(struct starfive_cryp_ctx), + .cra_alignmask = 0xf, + .cra_module = THIS_MODULE, +--- a/drivers/crypto/starfive/jh7110-cryp.h ++++ b/drivers/crypto/starfive/jh7110-cryp.h +@@ -50,8 +50,6 @@ union starfive_aes_csr { + u32 ccm_start :1; + #define STARFIVE_AES_MODE_ECB 0x0 + #define STARFIVE_AES_MODE_CBC 0x1 +-#define STARFIVE_AES_MODE_CFB 0x2 +-#define STARFIVE_AES_MODE_OFB 0x3 + #define STARFIVE_AES_MODE_CTR 0x4 + #define STARFIVE_AES_MODE_CCM 0x5 + #define STARFIVE_AES_MODE_GCM 0x6 diff --git a/target/linux/starfive/patches-6.6/0013-crypto-starfive-Remove-unneeded-NULL-checks.patch b/target/linux/starfive/patches-6.6/0013-crypto-starfive-Remove-unneeded-NULL-checks.patch new file mode 100644 index 0000000000..2044b72a35 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0013-crypto-starfive-Remove-unneeded-NULL-checks.patch @@ -0,0 +1,33 @@ +From 0dbdc763f10c5cfa968dffc290a7c060ee740172 Mon Sep 17 00:00:00 2001 +From: Jia Jie Ho +Date: Mon, 4 Dec 2023 11:02:39 +0800 +Subject: [PATCH 013/116] crypto: starfive - Remove unneeded NULL checks + +NULL check before kfree_sensitive function is not needed. + +Signed-off-by: Jia Jie Ho +Reported-by: kernel test robot +Closes: https://lore.kernel.org/oe-kbuild-all/202311301702.LxswfETY-lkp@intel.com/ +Signed-off-by: Herbert Xu +--- + drivers/crypto/starfive/jh7110-rsa.c | 9 +++------ + 1 file changed, 3 insertions(+), 6 deletions(-) + +--- a/drivers/crypto/starfive/jh7110-rsa.c ++++ b/drivers/crypto/starfive/jh7110-rsa.c +@@ -45,12 +45,9 @@ static inline int starfive_pka_wait_done + + static void starfive_rsa_free_key(struct starfive_rsa_key *key) + { +- if (key->d) +- kfree_sensitive(key->d); +- if (key->e) +- kfree_sensitive(key->e); +- if (key->n) +- kfree_sensitive(key->n); ++ kfree_sensitive(key->d); ++ kfree_sensitive(key->e); ++ kfree_sensitive(key->n); + memset(key, 0, sizeof(*key)); + } + diff --git a/target/linux/starfive/patches-6.6/0014-dt-bindings-PCI-Add-PLDA-XpressRICH-PCIe-host-common.patch b/target/linux/starfive/patches-6.6/0014-dt-bindings-PCI-Add-PLDA-XpressRICH-PCIe-host-common.patch new file mode 100644 index 0000000000..430571565a --- /dev/null +++ b/target/linux/starfive/patches-6.6/0014-dt-bindings-PCI-Add-PLDA-XpressRICH-PCIe-host-common.patch @@ -0,0 +1,183 @@ +From 708695ebf1a779de9a1fd2f72f7938afa6c14ada Mon Sep 17 00:00:00 2001 +From: Minda Chen +Date: Mon, 8 Jan 2024 19:05:51 +0800 +Subject: [PATCH 014/116] dt-bindings: PCI: Add PLDA XpressRICH PCIe host + common properties + +Add PLDA XpressRICH PCIe host common properties dt-binding doc. +PolarFire PCIe host using PLDA IP. Move common properties from Microchip +PolarFire PCIe host to PLDA files. + +Signed-off-by: Minda Chen +Reviewed-by: Hal Feng +Reviewed-by: Conor Dooley +Reviewed-by: Rob Herring +Tested-by: John Clark +--- + .../bindings/pci/microchip,pcie-host.yaml | 55 +------------- + .../pci/plda,xpressrich3-axi-common.yaml | 75 +++++++++++++++++++ + 2 files changed, 76 insertions(+), 54 deletions(-) + create mode 100644 Documentation/devicetree/bindings/pci/plda,xpressrich3-axi-common.yaml + +--- a/Documentation/devicetree/bindings/pci/microchip,pcie-host.yaml ++++ b/Documentation/devicetree/bindings/pci/microchip,pcie-host.yaml +@@ -10,21 +10,13 @@ maintainers: + - Daire McNamara + + allOf: +- - $ref: /schemas/pci/pci-bus.yaml# ++ - $ref: plda,xpressrich3-axi-common.yaml# + - $ref: /schemas/interrupt-controller/msi-controller.yaml# + + properties: + compatible: + const: microchip,pcie-host-1.0 # PolarFire + +- reg: +- maxItems: 2 +- +- reg-names: +- items: +- - const: cfg +- - const: apb +- + clocks: + description: + Fabric Interface Controllers, FICs, are the interface between the FPGA +@@ -52,18 +44,6 @@ properties: + items: + pattern: '^fic[0-3]$' + +- interrupts: +- minItems: 1 +- items: +- - description: PCIe host controller +- - description: builtin MSI controller +- +- interrupt-names: +- minItems: 1 +- items: +- - const: pcie +- - const: msi +- + ranges: + maxItems: 1 + +@@ -71,39 +51,6 @@ properties: + minItems: 1 + maxItems: 6 + +- msi-controller: +- description: Identifies the node as an MSI controller. +- +- msi-parent: +- description: MSI controller the device is capable of using. +- +- interrupt-controller: +- type: object +- properties: +- '#address-cells': +- const: 0 +- +- '#interrupt-cells': +- const: 1 +- +- interrupt-controller: true +- +- required: +- - '#address-cells' +- - '#interrupt-cells' +- - interrupt-controller +- +- additionalProperties: false +- +-required: +- - reg +- - reg-names +- - "#interrupt-cells" +- - interrupts +- - interrupt-map-mask +- - interrupt-map +- - msi-controller +- + unevaluatedProperties: false + + examples: +--- /dev/null ++++ b/Documentation/devicetree/bindings/pci/plda,xpressrich3-axi-common.yaml +@@ -0,0 +1,75 @@ ++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/pci/plda,xpressrich3-axi-common.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: PLDA XpressRICH PCIe host common properties ++ ++maintainers: ++ - Daire McNamara ++ - Kevin Xie ++ ++description: ++ Generic PLDA XpressRICH PCIe host common properties. ++ ++allOf: ++ - $ref: /schemas/pci/pci-bus.yaml# ++ ++properties: ++ reg: ++ maxItems: 2 ++ ++ reg-names: ++ items: ++ - const: cfg ++ - const: apb ++ ++ interrupts: ++ minItems: 1 ++ items: ++ - description: PCIe host controller ++ - description: builtin MSI controller ++ ++ interrupt-names: ++ minItems: 1 ++ items: ++ - const: pcie ++ - const: msi ++ ++ msi-controller: ++ description: Identifies the node as an MSI controller. ++ ++ msi-parent: ++ description: MSI controller the device is capable of using. ++ ++ interrupt-controller: ++ type: object ++ properties: ++ '#address-cells': ++ const: 0 ++ ++ '#interrupt-cells': ++ const: 1 ++ ++ interrupt-controller: true ++ ++ required: ++ - '#address-cells' ++ - '#interrupt-cells' ++ - interrupt-controller ++ ++ additionalProperties: false ++ ++required: ++ - reg ++ - reg-names ++ - interrupts ++ - msi-controller ++ - "#interrupt-cells" ++ - interrupt-map-mask ++ - interrupt-map ++ ++additionalProperties: true ++ ++... diff --git a/target/linux/starfive/patches-6.6/0015-PCI-microchip-Move-pcie-microchip-host.c-to-plda-dir.patch b/target/linux/starfive/patches-6.6/0015-PCI-microchip-Move-pcie-microchip-host.c-to-plda-dir.patch new file mode 100644 index 0000000000..9e7717fa5d --- /dev/null +++ b/target/linux/starfive/patches-6.6/0015-PCI-microchip-Move-pcie-microchip-host.c-to-plda-dir.patch @@ -0,0 +1,2523 @@ +From df67154aa92efdc774a8536ece6f431e7850aca2 Mon Sep 17 00:00:00 2001 +From: Minda Chen +Date: Mon, 8 Jan 2024 19:05:52 +0800 +Subject: [PATCH 015/116] PCI: microchip: Move pcie-microchip-host.c to plda + directory + +For Microchip Polarfire PCIe host is PLDA XpressRich IP, move to plda +directory. Prepare for refactoring the codes. + +Signed-off-by: Minda Chen +Reviewed-by: Conor Dooley +--- + drivers/pci/controller/Kconfig | 9 +-------- + drivers/pci/controller/Makefile | 2 +- + drivers/pci/controller/plda/Kconfig | 14 ++++++++++++++ + drivers/pci/controller/plda/Makefile | 2 ++ + .../controller/{ => plda}/pcie-microchip-host.c | 2 +- + 5 files changed, 19 insertions(+), 10 deletions(-) + create mode 100644 drivers/pci/controller/plda/Kconfig + create mode 100644 drivers/pci/controller/plda/Makefile + rename drivers/pci/controller/{ => plda}/pcie-microchip-host.c (99%) + +--- a/drivers/pci/controller/Kconfig ++++ b/drivers/pci/controller/Kconfig +@@ -215,14 +215,6 @@ config PCIE_MT7621 + help + This selects a driver for the MediaTek MT7621 PCIe Controller. + +-config PCIE_MICROCHIP_HOST +- tristate "Microchip AXI PCIe controller" +- depends on PCI_MSI && OF +- select PCI_HOST_COMMON +- help +- Say Y here if you want kernel to support the Microchip AXI PCIe +- Host Bridge driver. +- + config PCI_HYPERV_INTERFACE + tristate "Microsoft Hyper-V PCI Interface" + depends on ((X86 && X86_64) || ARM64) && HYPERV && PCI_MSI +@@ -345,4 +337,5 @@ config PCIE_XILINX_CPM + source "drivers/pci/controller/cadence/Kconfig" + source "drivers/pci/controller/dwc/Kconfig" + source "drivers/pci/controller/mobiveil/Kconfig" ++source "drivers/pci/controller/plda/Kconfig" + endmenu +--- a/drivers/pci/controller/Makefile ++++ b/drivers/pci/controller/Makefile +@@ -32,7 +32,6 @@ obj-$(CONFIG_PCIE_ROCKCHIP_EP) += pcie-r + obj-$(CONFIG_PCIE_ROCKCHIP_HOST) += pcie-rockchip-host.o + obj-$(CONFIG_PCIE_MEDIATEK) += pcie-mediatek.o + obj-$(CONFIG_PCIE_MEDIATEK_GEN3) += pcie-mediatek-gen3.o +-obj-$(CONFIG_PCIE_MICROCHIP_HOST) += pcie-microchip-host.o + obj-$(CONFIG_VMD) += vmd.o + obj-$(CONFIG_PCIE_BRCMSTB) += pcie-brcmstb.o + obj-$(CONFIG_PCI_LOONGSON) += pci-loongson.o +@@ -43,6 +42,7 @@ obj-$(CONFIG_PCIE_MT7621) += pcie-mt7621 + # pcie-hisi.o quirks are needed even without CONFIG_PCIE_DW + obj-y += dwc/ + obj-y += mobiveil/ ++obj-y += plda/ + + + # The following drivers are for devices that use the generic ACPI +--- /dev/null ++++ b/drivers/pci/controller/plda/Kconfig +@@ -0,0 +1,14 @@ ++# SPDX-License-Identifier: GPL-2.0 ++ ++menu "PLDA-based PCIe controllers" ++ depends on PCI ++ ++config PCIE_MICROCHIP_HOST ++ tristate "Microchip AXI PCIe controller" ++ depends on PCI_MSI && OF ++ select PCI_HOST_COMMON ++ help ++ Say Y here if you want kernel to support the Microchip AXI PCIe ++ Host Bridge driver. ++ ++endmenu +--- /dev/null ++++ b/drivers/pci/controller/plda/Makefile +@@ -0,0 +1,2 @@ ++# SPDX-License-Identifier: GPL-2.0 ++obj-$(CONFIG_PCIE_MICROCHIP_HOST) += pcie-microchip-host.o +--- a/drivers/pci/controller/pcie-microchip-host.c ++++ /dev/null +@@ -1,1216 +0,0 @@ +-// SPDX-License-Identifier: GPL-2.0 +-/* +- * Microchip AXI PCIe Bridge host controller driver +- * +- * Copyright (c) 2018 - 2020 Microchip Corporation. All rights reserved. +- * +- * Author: Daire McNamara +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "../pci.h" +- +-/* Number of MSI IRQs */ +-#define MC_MAX_NUM_MSI_IRQS 32 +- +-/* PCIe Bridge Phy and Controller Phy offsets */ +-#define MC_PCIE1_BRIDGE_ADDR 0x00008000u +-#define MC_PCIE1_CTRL_ADDR 0x0000a000u +- +-#define MC_PCIE_BRIDGE_ADDR (MC_PCIE1_BRIDGE_ADDR) +-#define MC_PCIE_CTRL_ADDR (MC_PCIE1_CTRL_ADDR) +- +-/* PCIe Bridge Phy Regs */ +-#define PCIE_PCI_IRQ_DW0 0xa8 +-#define MSIX_CAP_MASK BIT(31) +-#define NUM_MSI_MSGS_MASK GENMASK(6, 4) +-#define NUM_MSI_MSGS_SHIFT 4 +- +-#define IMASK_LOCAL 0x180 +-#define DMA_END_ENGINE_0_MASK 0x00000000u +-#define DMA_END_ENGINE_0_SHIFT 0 +-#define DMA_END_ENGINE_1_MASK 0x00000000u +-#define DMA_END_ENGINE_1_SHIFT 1 +-#define DMA_ERROR_ENGINE_0_MASK 0x00000100u +-#define DMA_ERROR_ENGINE_0_SHIFT 8 +-#define DMA_ERROR_ENGINE_1_MASK 0x00000200u +-#define DMA_ERROR_ENGINE_1_SHIFT 9 +-#define A_ATR_EVT_POST_ERR_MASK 0x00010000u +-#define A_ATR_EVT_POST_ERR_SHIFT 16 +-#define A_ATR_EVT_FETCH_ERR_MASK 0x00020000u +-#define A_ATR_EVT_FETCH_ERR_SHIFT 17 +-#define A_ATR_EVT_DISCARD_ERR_MASK 0x00040000u +-#define A_ATR_EVT_DISCARD_ERR_SHIFT 18 +-#define A_ATR_EVT_DOORBELL_MASK 0x00000000u +-#define A_ATR_EVT_DOORBELL_SHIFT 19 +-#define P_ATR_EVT_POST_ERR_MASK 0x00100000u +-#define P_ATR_EVT_POST_ERR_SHIFT 20 +-#define P_ATR_EVT_FETCH_ERR_MASK 0x00200000u +-#define P_ATR_EVT_FETCH_ERR_SHIFT 21 +-#define P_ATR_EVT_DISCARD_ERR_MASK 0x00400000u +-#define P_ATR_EVT_DISCARD_ERR_SHIFT 22 +-#define P_ATR_EVT_DOORBELL_MASK 0x00000000u +-#define P_ATR_EVT_DOORBELL_SHIFT 23 +-#define PM_MSI_INT_INTA_MASK 0x01000000u +-#define PM_MSI_INT_INTA_SHIFT 24 +-#define PM_MSI_INT_INTB_MASK 0x02000000u +-#define PM_MSI_INT_INTB_SHIFT 25 +-#define PM_MSI_INT_INTC_MASK 0x04000000u +-#define PM_MSI_INT_INTC_SHIFT 26 +-#define PM_MSI_INT_INTD_MASK 0x08000000u +-#define PM_MSI_INT_INTD_SHIFT 27 +-#define PM_MSI_INT_INTX_MASK 0x0f000000u +-#define PM_MSI_INT_INTX_SHIFT 24 +-#define PM_MSI_INT_MSI_MASK 0x10000000u +-#define PM_MSI_INT_MSI_SHIFT 28 +-#define PM_MSI_INT_AER_EVT_MASK 0x20000000u +-#define PM_MSI_INT_AER_EVT_SHIFT 29 +-#define PM_MSI_INT_EVENTS_MASK 0x40000000u +-#define PM_MSI_INT_EVENTS_SHIFT 30 +-#define PM_MSI_INT_SYS_ERR_MASK 0x80000000u +-#define PM_MSI_INT_SYS_ERR_SHIFT 31 +-#define NUM_LOCAL_EVENTS 15 +-#define ISTATUS_LOCAL 0x184 +-#define IMASK_HOST 0x188 +-#define ISTATUS_HOST 0x18c +-#define IMSI_ADDR 0x190 +-#define ISTATUS_MSI 0x194 +- +-/* PCIe Master table init defines */ +-#define ATR0_PCIE_WIN0_SRCADDR_PARAM 0x600u +-#define ATR0_PCIE_ATR_SIZE 0x25 +-#define ATR0_PCIE_ATR_SIZE_SHIFT 1 +-#define ATR0_PCIE_WIN0_SRC_ADDR 0x604u +-#define ATR0_PCIE_WIN0_TRSL_ADDR_LSB 0x608u +-#define ATR0_PCIE_WIN0_TRSL_ADDR_UDW 0x60cu +-#define ATR0_PCIE_WIN0_TRSL_PARAM 0x610u +- +-/* PCIe AXI slave table init defines */ +-#define ATR0_AXI4_SLV0_SRCADDR_PARAM 0x800u +-#define ATR_SIZE_SHIFT 1 +-#define ATR_IMPL_ENABLE 1 +-#define ATR0_AXI4_SLV0_SRC_ADDR 0x804u +-#define ATR0_AXI4_SLV0_TRSL_ADDR_LSB 0x808u +-#define ATR0_AXI4_SLV0_TRSL_ADDR_UDW 0x80cu +-#define ATR0_AXI4_SLV0_TRSL_PARAM 0x810u +-#define PCIE_TX_RX_INTERFACE 0x00000000u +-#define PCIE_CONFIG_INTERFACE 0x00000001u +- +-#define ATR_ENTRY_SIZE 32 +- +-/* PCIe Controller Phy Regs */ +-#define SEC_ERROR_EVENT_CNT 0x20 +-#define DED_ERROR_EVENT_CNT 0x24 +-#define SEC_ERROR_INT 0x28 +-#define SEC_ERROR_INT_TX_RAM_SEC_ERR_INT GENMASK(3, 0) +-#define SEC_ERROR_INT_RX_RAM_SEC_ERR_INT GENMASK(7, 4) +-#define SEC_ERROR_INT_PCIE2AXI_RAM_SEC_ERR_INT GENMASK(11, 8) +-#define SEC_ERROR_INT_AXI2PCIE_RAM_SEC_ERR_INT GENMASK(15, 12) +-#define SEC_ERROR_INT_ALL_RAM_SEC_ERR_INT GENMASK(15, 0) +-#define NUM_SEC_ERROR_INTS (4) +-#define SEC_ERROR_INT_MASK 0x2c +-#define DED_ERROR_INT 0x30 +-#define DED_ERROR_INT_TX_RAM_DED_ERR_INT GENMASK(3, 0) +-#define DED_ERROR_INT_RX_RAM_DED_ERR_INT GENMASK(7, 4) +-#define DED_ERROR_INT_PCIE2AXI_RAM_DED_ERR_INT GENMASK(11, 8) +-#define DED_ERROR_INT_AXI2PCIE_RAM_DED_ERR_INT GENMASK(15, 12) +-#define DED_ERROR_INT_ALL_RAM_DED_ERR_INT GENMASK(15, 0) +-#define NUM_DED_ERROR_INTS (4) +-#define DED_ERROR_INT_MASK 0x34 +-#define ECC_CONTROL 0x38 +-#define ECC_CONTROL_TX_RAM_INJ_ERROR_0 BIT(0) +-#define ECC_CONTROL_TX_RAM_INJ_ERROR_1 BIT(1) +-#define ECC_CONTROL_TX_RAM_INJ_ERROR_2 BIT(2) +-#define ECC_CONTROL_TX_RAM_INJ_ERROR_3 BIT(3) +-#define ECC_CONTROL_RX_RAM_INJ_ERROR_0 BIT(4) +-#define ECC_CONTROL_RX_RAM_INJ_ERROR_1 BIT(5) +-#define ECC_CONTROL_RX_RAM_INJ_ERROR_2 BIT(6) +-#define ECC_CONTROL_RX_RAM_INJ_ERROR_3 BIT(7) +-#define ECC_CONTROL_PCIE2AXI_RAM_INJ_ERROR_0 BIT(8) +-#define ECC_CONTROL_PCIE2AXI_RAM_INJ_ERROR_1 BIT(9) +-#define ECC_CONTROL_PCIE2AXI_RAM_INJ_ERROR_2 BIT(10) +-#define ECC_CONTROL_PCIE2AXI_RAM_INJ_ERROR_3 BIT(11) +-#define ECC_CONTROL_AXI2PCIE_RAM_INJ_ERROR_0 BIT(12) +-#define ECC_CONTROL_AXI2PCIE_RAM_INJ_ERROR_1 BIT(13) +-#define ECC_CONTROL_AXI2PCIE_RAM_INJ_ERROR_2 BIT(14) +-#define ECC_CONTROL_AXI2PCIE_RAM_INJ_ERROR_3 BIT(15) +-#define ECC_CONTROL_TX_RAM_ECC_BYPASS BIT(24) +-#define ECC_CONTROL_RX_RAM_ECC_BYPASS BIT(25) +-#define ECC_CONTROL_PCIE2AXI_RAM_ECC_BYPASS BIT(26) +-#define ECC_CONTROL_AXI2PCIE_RAM_ECC_BYPASS BIT(27) +-#define PCIE_EVENT_INT 0x14c +-#define PCIE_EVENT_INT_L2_EXIT_INT BIT(0) +-#define PCIE_EVENT_INT_HOTRST_EXIT_INT BIT(1) +-#define PCIE_EVENT_INT_DLUP_EXIT_INT BIT(2) +-#define PCIE_EVENT_INT_MASK GENMASK(2, 0) +-#define PCIE_EVENT_INT_L2_EXIT_INT_MASK BIT(16) +-#define PCIE_EVENT_INT_HOTRST_EXIT_INT_MASK BIT(17) +-#define PCIE_EVENT_INT_DLUP_EXIT_INT_MASK BIT(18) +-#define PCIE_EVENT_INT_ENB_MASK GENMASK(18, 16) +-#define PCIE_EVENT_INT_ENB_SHIFT 16 +-#define NUM_PCIE_EVENTS (3) +- +-/* PCIe Config space MSI capability structure */ +-#define MC_MSI_CAP_CTRL_OFFSET 0xe0u +- +-/* Events */ +-#define EVENT_PCIE_L2_EXIT 0 +-#define EVENT_PCIE_HOTRST_EXIT 1 +-#define EVENT_PCIE_DLUP_EXIT 2 +-#define EVENT_SEC_TX_RAM_SEC_ERR 3 +-#define EVENT_SEC_RX_RAM_SEC_ERR 4 +-#define EVENT_SEC_PCIE2AXI_RAM_SEC_ERR 5 +-#define EVENT_SEC_AXI2PCIE_RAM_SEC_ERR 6 +-#define EVENT_DED_TX_RAM_DED_ERR 7 +-#define EVENT_DED_RX_RAM_DED_ERR 8 +-#define EVENT_DED_PCIE2AXI_RAM_DED_ERR 9 +-#define EVENT_DED_AXI2PCIE_RAM_DED_ERR 10 +-#define EVENT_LOCAL_DMA_END_ENGINE_0 11 +-#define EVENT_LOCAL_DMA_END_ENGINE_1 12 +-#define EVENT_LOCAL_DMA_ERROR_ENGINE_0 13 +-#define EVENT_LOCAL_DMA_ERROR_ENGINE_1 14 +-#define EVENT_LOCAL_A_ATR_EVT_POST_ERR 15 +-#define EVENT_LOCAL_A_ATR_EVT_FETCH_ERR 16 +-#define EVENT_LOCAL_A_ATR_EVT_DISCARD_ERR 17 +-#define EVENT_LOCAL_A_ATR_EVT_DOORBELL 18 +-#define EVENT_LOCAL_P_ATR_EVT_POST_ERR 19 +-#define EVENT_LOCAL_P_ATR_EVT_FETCH_ERR 20 +-#define EVENT_LOCAL_P_ATR_EVT_DISCARD_ERR 21 +-#define EVENT_LOCAL_P_ATR_EVT_DOORBELL 22 +-#define EVENT_LOCAL_PM_MSI_INT_INTX 23 +-#define EVENT_LOCAL_PM_MSI_INT_MSI 24 +-#define EVENT_LOCAL_PM_MSI_INT_AER_EVT 25 +-#define EVENT_LOCAL_PM_MSI_INT_EVENTS 26 +-#define EVENT_LOCAL_PM_MSI_INT_SYS_ERR 27 +-#define NUM_EVENTS 28 +- +-#define PCIE_EVENT_CAUSE(x, s) \ +- [EVENT_PCIE_ ## x] = { __stringify(x), s } +- +-#define SEC_ERROR_CAUSE(x, s) \ +- [EVENT_SEC_ ## x] = { __stringify(x), s } +- +-#define DED_ERROR_CAUSE(x, s) \ +- [EVENT_DED_ ## x] = { __stringify(x), s } +- +-#define LOCAL_EVENT_CAUSE(x, s) \ +- [EVENT_LOCAL_ ## x] = { __stringify(x), s } +- +-#define PCIE_EVENT(x) \ +- .base = MC_PCIE_CTRL_ADDR, \ +- .offset = PCIE_EVENT_INT, \ +- .mask_offset = PCIE_EVENT_INT, \ +- .mask_high = 1, \ +- .mask = PCIE_EVENT_INT_ ## x ## _INT, \ +- .enb_mask = PCIE_EVENT_INT_ENB_MASK +- +-#define SEC_EVENT(x) \ +- .base = MC_PCIE_CTRL_ADDR, \ +- .offset = SEC_ERROR_INT, \ +- .mask_offset = SEC_ERROR_INT_MASK, \ +- .mask = SEC_ERROR_INT_ ## x ## _INT, \ +- .mask_high = 1, \ +- .enb_mask = 0 +- +-#define DED_EVENT(x) \ +- .base = MC_PCIE_CTRL_ADDR, \ +- .offset = DED_ERROR_INT, \ +- .mask_offset = DED_ERROR_INT_MASK, \ +- .mask_high = 1, \ +- .mask = DED_ERROR_INT_ ## x ## _INT, \ +- .enb_mask = 0 +- +-#define LOCAL_EVENT(x) \ +- .base = MC_PCIE_BRIDGE_ADDR, \ +- .offset = ISTATUS_LOCAL, \ +- .mask_offset = IMASK_LOCAL, \ +- .mask_high = 0, \ +- .mask = x ## _MASK, \ +- .enb_mask = 0 +- +-#define PCIE_EVENT_TO_EVENT_MAP(x) \ +- { PCIE_EVENT_INT_ ## x ## _INT, EVENT_PCIE_ ## x } +- +-#define SEC_ERROR_TO_EVENT_MAP(x) \ +- { SEC_ERROR_INT_ ## x ## _INT, EVENT_SEC_ ## x } +- +-#define DED_ERROR_TO_EVENT_MAP(x) \ +- { DED_ERROR_INT_ ## x ## _INT, EVENT_DED_ ## x } +- +-#define LOCAL_STATUS_TO_EVENT_MAP(x) \ +- { x ## _MASK, EVENT_LOCAL_ ## x } +- +-struct event_map { +- u32 reg_mask; +- u32 event_bit; +-}; +- +-struct mc_msi { +- struct mutex lock; /* Protect used bitmap */ +- struct irq_domain *msi_domain; +- struct irq_domain *dev_domain; +- u32 num_vectors; +- u64 vector_phy; +- DECLARE_BITMAP(used, MC_MAX_NUM_MSI_IRQS); +-}; +- +-struct mc_pcie { +- void __iomem *axi_base_addr; +- struct device *dev; +- struct irq_domain *intx_domain; +- struct irq_domain *event_domain; +- raw_spinlock_t lock; +- struct mc_msi msi; +-}; +- +-struct cause { +- const char *sym; +- const char *str; +-}; +- +-static const struct cause event_cause[NUM_EVENTS] = { +- PCIE_EVENT_CAUSE(L2_EXIT, "L2 exit event"), +- PCIE_EVENT_CAUSE(HOTRST_EXIT, "Hot reset exit event"), +- PCIE_EVENT_CAUSE(DLUP_EXIT, "DLUP exit event"), +- SEC_ERROR_CAUSE(TX_RAM_SEC_ERR, "sec error in tx buffer"), +- SEC_ERROR_CAUSE(RX_RAM_SEC_ERR, "sec error in rx buffer"), +- SEC_ERROR_CAUSE(PCIE2AXI_RAM_SEC_ERR, "sec error in pcie2axi buffer"), +- SEC_ERROR_CAUSE(AXI2PCIE_RAM_SEC_ERR, "sec error in axi2pcie buffer"), +- DED_ERROR_CAUSE(TX_RAM_DED_ERR, "ded error in tx buffer"), +- DED_ERROR_CAUSE(RX_RAM_DED_ERR, "ded error in rx buffer"), +- DED_ERROR_CAUSE(PCIE2AXI_RAM_DED_ERR, "ded error in pcie2axi buffer"), +- DED_ERROR_CAUSE(AXI2PCIE_RAM_DED_ERR, "ded error in axi2pcie buffer"), +- LOCAL_EVENT_CAUSE(DMA_ERROR_ENGINE_0, "dma engine 0 error"), +- LOCAL_EVENT_CAUSE(DMA_ERROR_ENGINE_1, "dma engine 1 error"), +- LOCAL_EVENT_CAUSE(A_ATR_EVT_POST_ERR, "axi write request error"), +- LOCAL_EVENT_CAUSE(A_ATR_EVT_FETCH_ERR, "axi read request error"), +- LOCAL_EVENT_CAUSE(A_ATR_EVT_DISCARD_ERR, "axi read timeout"), +- LOCAL_EVENT_CAUSE(P_ATR_EVT_POST_ERR, "pcie write request error"), +- LOCAL_EVENT_CAUSE(P_ATR_EVT_FETCH_ERR, "pcie read request error"), +- LOCAL_EVENT_CAUSE(P_ATR_EVT_DISCARD_ERR, "pcie read timeout"), +- LOCAL_EVENT_CAUSE(PM_MSI_INT_AER_EVT, "aer event"), +- LOCAL_EVENT_CAUSE(PM_MSI_INT_EVENTS, "pm/ltr/hotplug event"), +- LOCAL_EVENT_CAUSE(PM_MSI_INT_SYS_ERR, "system error"), +-}; +- +-static struct event_map pcie_event_to_event[] = { +- PCIE_EVENT_TO_EVENT_MAP(L2_EXIT), +- PCIE_EVENT_TO_EVENT_MAP(HOTRST_EXIT), +- PCIE_EVENT_TO_EVENT_MAP(DLUP_EXIT), +-}; +- +-static struct event_map sec_error_to_event[] = { +- SEC_ERROR_TO_EVENT_MAP(TX_RAM_SEC_ERR), +- SEC_ERROR_TO_EVENT_MAP(RX_RAM_SEC_ERR), +- SEC_ERROR_TO_EVENT_MAP(PCIE2AXI_RAM_SEC_ERR), +- SEC_ERROR_TO_EVENT_MAP(AXI2PCIE_RAM_SEC_ERR), +-}; +- +-static struct event_map ded_error_to_event[] = { +- DED_ERROR_TO_EVENT_MAP(TX_RAM_DED_ERR), +- DED_ERROR_TO_EVENT_MAP(RX_RAM_DED_ERR), +- DED_ERROR_TO_EVENT_MAP(PCIE2AXI_RAM_DED_ERR), +- DED_ERROR_TO_EVENT_MAP(AXI2PCIE_RAM_DED_ERR), +-}; +- +-static struct event_map local_status_to_event[] = { +- LOCAL_STATUS_TO_EVENT_MAP(DMA_END_ENGINE_0), +- LOCAL_STATUS_TO_EVENT_MAP(DMA_END_ENGINE_1), +- LOCAL_STATUS_TO_EVENT_MAP(DMA_ERROR_ENGINE_0), +- LOCAL_STATUS_TO_EVENT_MAP(DMA_ERROR_ENGINE_1), +- LOCAL_STATUS_TO_EVENT_MAP(A_ATR_EVT_POST_ERR), +- LOCAL_STATUS_TO_EVENT_MAP(A_ATR_EVT_FETCH_ERR), +- LOCAL_STATUS_TO_EVENT_MAP(A_ATR_EVT_DISCARD_ERR), +- LOCAL_STATUS_TO_EVENT_MAP(A_ATR_EVT_DOORBELL), +- LOCAL_STATUS_TO_EVENT_MAP(P_ATR_EVT_POST_ERR), +- LOCAL_STATUS_TO_EVENT_MAP(P_ATR_EVT_FETCH_ERR), +- LOCAL_STATUS_TO_EVENT_MAP(P_ATR_EVT_DISCARD_ERR), +- LOCAL_STATUS_TO_EVENT_MAP(P_ATR_EVT_DOORBELL), +- LOCAL_STATUS_TO_EVENT_MAP(PM_MSI_INT_INTX), +- LOCAL_STATUS_TO_EVENT_MAP(PM_MSI_INT_MSI), +- LOCAL_STATUS_TO_EVENT_MAP(PM_MSI_INT_AER_EVT), +- LOCAL_STATUS_TO_EVENT_MAP(PM_MSI_INT_EVENTS), +- LOCAL_STATUS_TO_EVENT_MAP(PM_MSI_INT_SYS_ERR), +-}; +- +-static struct { +- u32 base; +- u32 offset; +- u32 mask; +- u32 shift; +- u32 enb_mask; +- u32 mask_high; +- u32 mask_offset; +-} event_descs[] = { +- { PCIE_EVENT(L2_EXIT) }, +- { PCIE_EVENT(HOTRST_EXIT) }, +- { PCIE_EVENT(DLUP_EXIT) }, +- { SEC_EVENT(TX_RAM_SEC_ERR) }, +- { SEC_EVENT(RX_RAM_SEC_ERR) }, +- { SEC_EVENT(PCIE2AXI_RAM_SEC_ERR) }, +- { SEC_EVENT(AXI2PCIE_RAM_SEC_ERR) }, +- { DED_EVENT(TX_RAM_DED_ERR) }, +- { DED_EVENT(RX_RAM_DED_ERR) }, +- { DED_EVENT(PCIE2AXI_RAM_DED_ERR) }, +- { DED_EVENT(AXI2PCIE_RAM_DED_ERR) }, +- { LOCAL_EVENT(DMA_END_ENGINE_0) }, +- { LOCAL_EVENT(DMA_END_ENGINE_1) }, +- { LOCAL_EVENT(DMA_ERROR_ENGINE_0) }, +- { LOCAL_EVENT(DMA_ERROR_ENGINE_1) }, +- { LOCAL_EVENT(A_ATR_EVT_POST_ERR) }, +- { LOCAL_EVENT(A_ATR_EVT_FETCH_ERR) }, +- { LOCAL_EVENT(A_ATR_EVT_DISCARD_ERR) }, +- { LOCAL_EVENT(A_ATR_EVT_DOORBELL) }, +- { LOCAL_EVENT(P_ATR_EVT_POST_ERR) }, +- { LOCAL_EVENT(P_ATR_EVT_FETCH_ERR) }, +- { LOCAL_EVENT(P_ATR_EVT_DISCARD_ERR) }, +- { LOCAL_EVENT(P_ATR_EVT_DOORBELL) }, +- { LOCAL_EVENT(PM_MSI_INT_INTX) }, +- { LOCAL_EVENT(PM_MSI_INT_MSI) }, +- { LOCAL_EVENT(PM_MSI_INT_AER_EVT) }, +- { LOCAL_EVENT(PM_MSI_INT_EVENTS) }, +- { LOCAL_EVENT(PM_MSI_INT_SYS_ERR) }, +-}; +- +-static char poss_clks[][5] = { "fic0", "fic1", "fic2", "fic3" }; +- +-static struct mc_pcie *port; +- +-static void mc_pcie_enable_msi(struct mc_pcie *port, void __iomem *ecam) +-{ +- struct mc_msi *msi = &port->msi; +- u16 reg; +- u8 queue_size; +- +- /* Fixup MSI enable flag */ +- reg = readw_relaxed(ecam + MC_MSI_CAP_CTRL_OFFSET + PCI_MSI_FLAGS); +- reg |= PCI_MSI_FLAGS_ENABLE; +- writew_relaxed(reg, ecam + MC_MSI_CAP_CTRL_OFFSET + PCI_MSI_FLAGS); +- +- /* Fixup PCI MSI queue flags */ +- queue_size = FIELD_GET(PCI_MSI_FLAGS_QMASK, reg); +- reg |= FIELD_PREP(PCI_MSI_FLAGS_QSIZE, queue_size); +- writew_relaxed(reg, ecam + MC_MSI_CAP_CTRL_OFFSET + PCI_MSI_FLAGS); +- +- /* Fixup MSI addr fields */ +- writel_relaxed(lower_32_bits(msi->vector_phy), +- ecam + MC_MSI_CAP_CTRL_OFFSET + PCI_MSI_ADDRESS_LO); +- writel_relaxed(upper_32_bits(msi->vector_phy), +- ecam + MC_MSI_CAP_CTRL_OFFSET + PCI_MSI_ADDRESS_HI); +-} +- +-static void mc_handle_msi(struct irq_desc *desc) +-{ +- struct mc_pcie *port = irq_desc_get_handler_data(desc); +- struct irq_chip *chip = irq_desc_get_chip(desc); +- struct device *dev = port->dev; +- struct mc_msi *msi = &port->msi; +- void __iomem *bridge_base_addr = +- port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; +- unsigned long status; +- u32 bit; +- int ret; +- +- chained_irq_enter(chip, desc); +- +- status = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL); +- if (status & PM_MSI_INT_MSI_MASK) { +- writel_relaxed(status & PM_MSI_INT_MSI_MASK, bridge_base_addr + ISTATUS_LOCAL); +- status = readl_relaxed(bridge_base_addr + ISTATUS_MSI); +- for_each_set_bit(bit, &status, msi->num_vectors) { +- ret = generic_handle_domain_irq(msi->dev_domain, bit); +- if (ret) +- dev_err_ratelimited(dev, "bad MSI IRQ %d\n", +- bit); +- } +- } +- +- chained_irq_exit(chip, desc); +-} +- +-static void mc_msi_bottom_irq_ack(struct irq_data *data) +-{ +- struct mc_pcie *port = irq_data_get_irq_chip_data(data); +- void __iomem *bridge_base_addr = +- port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; +- u32 bitpos = data->hwirq; +- +- writel_relaxed(BIT(bitpos), bridge_base_addr + ISTATUS_MSI); +-} +- +-static void mc_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) +-{ +- struct mc_pcie *port = irq_data_get_irq_chip_data(data); +- phys_addr_t addr = port->msi.vector_phy; +- +- msg->address_lo = lower_32_bits(addr); +- msg->address_hi = upper_32_bits(addr); +- msg->data = data->hwirq; +- +- dev_dbg(port->dev, "msi#%x address_hi %#x address_lo %#x\n", +- (int)data->hwirq, msg->address_hi, msg->address_lo); +-} +- +-static int mc_msi_set_affinity(struct irq_data *irq_data, +- const struct cpumask *mask, bool force) +-{ +- return -EINVAL; +-} +- +-static struct irq_chip mc_msi_bottom_irq_chip = { +- .name = "Microchip MSI", +- .irq_ack = mc_msi_bottom_irq_ack, +- .irq_compose_msi_msg = mc_compose_msi_msg, +- .irq_set_affinity = mc_msi_set_affinity, +-}; +- +-static int mc_irq_msi_domain_alloc(struct irq_domain *domain, unsigned int virq, +- unsigned int nr_irqs, void *args) +-{ +- struct mc_pcie *port = domain->host_data; +- struct mc_msi *msi = &port->msi; +- unsigned long bit; +- +- mutex_lock(&msi->lock); +- bit = find_first_zero_bit(msi->used, msi->num_vectors); +- if (bit >= msi->num_vectors) { +- mutex_unlock(&msi->lock); +- return -ENOSPC; +- } +- +- set_bit(bit, msi->used); +- +- irq_domain_set_info(domain, virq, bit, &mc_msi_bottom_irq_chip, +- domain->host_data, handle_edge_irq, NULL, NULL); +- +- mutex_unlock(&msi->lock); +- +- return 0; +-} +- +-static void mc_irq_msi_domain_free(struct irq_domain *domain, unsigned int virq, +- unsigned int nr_irqs) +-{ +- struct irq_data *d = irq_domain_get_irq_data(domain, virq); +- struct mc_pcie *port = irq_data_get_irq_chip_data(d); +- struct mc_msi *msi = &port->msi; +- +- mutex_lock(&msi->lock); +- +- if (test_bit(d->hwirq, msi->used)) +- __clear_bit(d->hwirq, msi->used); +- else +- dev_err(port->dev, "trying to free unused MSI%lu\n", d->hwirq); +- +- mutex_unlock(&msi->lock); +-} +- +-static const struct irq_domain_ops msi_domain_ops = { +- .alloc = mc_irq_msi_domain_alloc, +- .free = mc_irq_msi_domain_free, +-}; +- +-static struct irq_chip mc_msi_irq_chip = { +- .name = "Microchip PCIe MSI", +- .irq_ack = irq_chip_ack_parent, +- .irq_mask = pci_msi_mask_irq, +- .irq_unmask = pci_msi_unmask_irq, +-}; +- +-static struct msi_domain_info mc_msi_domain_info = { +- .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | +- MSI_FLAG_PCI_MSIX), +- .chip = &mc_msi_irq_chip, +-}; +- +-static int mc_allocate_msi_domains(struct mc_pcie *port) +-{ +- struct device *dev = port->dev; +- struct fwnode_handle *fwnode = of_node_to_fwnode(dev->of_node); +- struct mc_msi *msi = &port->msi; +- +- mutex_init(&port->msi.lock); +- +- msi->dev_domain = irq_domain_add_linear(NULL, msi->num_vectors, +- &msi_domain_ops, port); +- if (!msi->dev_domain) { +- dev_err(dev, "failed to create IRQ domain\n"); +- return -ENOMEM; +- } +- +- msi->msi_domain = pci_msi_create_irq_domain(fwnode, &mc_msi_domain_info, +- msi->dev_domain); +- if (!msi->msi_domain) { +- dev_err(dev, "failed to create MSI domain\n"); +- irq_domain_remove(msi->dev_domain); +- return -ENOMEM; +- } +- +- return 0; +-} +- +-static void mc_handle_intx(struct irq_desc *desc) +-{ +- struct mc_pcie *port = irq_desc_get_handler_data(desc); +- struct irq_chip *chip = irq_desc_get_chip(desc); +- struct device *dev = port->dev; +- void __iomem *bridge_base_addr = +- port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; +- unsigned long status; +- u32 bit; +- int ret; +- +- chained_irq_enter(chip, desc); +- +- status = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL); +- if (status & PM_MSI_INT_INTX_MASK) { +- status &= PM_MSI_INT_INTX_MASK; +- status >>= PM_MSI_INT_INTX_SHIFT; +- for_each_set_bit(bit, &status, PCI_NUM_INTX) { +- ret = generic_handle_domain_irq(port->intx_domain, bit); +- if (ret) +- dev_err_ratelimited(dev, "bad INTx IRQ %d\n", +- bit); +- } +- } +- +- chained_irq_exit(chip, desc); +-} +- +-static void mc_ack_intx_irq(struct irq_data *data) +-{ +- struct mc_pcie *port = irq_data_get_irq_chip_data(data); +- void __iomem *bridge_base_addr = +- port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; +- u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT); +- +- writel_relaxed(mask, bridge_base_addr + ISTATUS_LOCAL); +-} +- +-static void mc_mask_intx_irq(struct irq_data *data) +-{ +- struct mc_pcie *port = irq_data_get_irq_chip_data(data); +- void __iomem *bridge_base_addr = +- port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; +- unsigned long flags; +- u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT); +- u32 val; +- +- raw_spin_lock_irqsave(&port->lock, flags); +- val = readl_relaxed(bridge_base_addr + IMASK_LOCAL); +- val &= ~mask; +- writel_relaxed(val, bridge_base_addr + IMASK_LOCAL); +- raw_spin_unlock_irqrestore(&port->lock, flags); +-} +- +-static void mc_unmask_intx_irq(struct irq_data *data) +-{ +- struct mc_pcie *port = irq_data_get_irq_chip_data(data); +- void __iomem *bridge_base_addr = +- port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; +- unsigned long flags; +- u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT); +- u32 val; +- +- raw_spin_lock_irqsave(&port->lock, flags); +- val = readl_relaxed(bridge_base_addr + IMASK_LOCAL); +- val |= mask; +- writel_relaxed(val, bridge_base_addr + IMASK_LOCAL); +- raw_spin_unlock_irqrestore(&port->lock, flags); +-} +- +-static struct irq_chip mc_intx_irq_chip = { +- .name = "Microchip PCIe INTx", +- .irq_ack = mc_ack_intx_irq, +- .irq_mask = mc_mask_intx_irq, +- .irq_unmask = mc_unmask_intx_irq, +-}; +- +-static int mc_pcie_intx_map(struct irq_domain *domain, unsigned int irq, +- irq_hw_number_t hwirq) +-{ +- irq_set_chip_and_handler(irq, &mc_intx_irq_chip, handle_level_irq); +- irq_set_chip_data(irq, domain->host_data); +- +- return 0; +-} +- +-static const struct irq_domain_ops intx_domain_ops = { +- .map = mc_pcie_intx_map, +-}; +- +-static inline u32 reg_to_event(u32 reg, struct event_map field) +-{ +- return (reg & field.reg_mask) ? BIT(field.event_bit) : 0; +-} +- +-static u32 pcie_events(struct mc_pcie *port) +-{ +- void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR; +- u32 reg = readl_relaxed(ctrl_base_addr + PCIE_EVENT_INT); +- u32 val = 0; +- int i; +- +- for (i = 0; i < ARRAY_SIZE(pcie_event_to_event); i++) +- val |= reg_to_event(reg, pcie_event_to_event[i]); +- +- return val; +-} +- +-static u32 sec_errors(struct mc_pcie *port) +-{ +- void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR; +- u32 reg = readl_relaxed(ctrl_base_addr + SEC_ERROR_INT); +- u32 val = 0; +- int i; +- +- for (i = 0; i < ARRAY_SIZE(sec_error_to_event); i++) +- val |= reg_to_event(reg, sec_error_to_event[i]); +- +- return val; +-} +- +-static u32 ded_errors(struct mc_pcie *port) +-{ +- void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR; +- u32 reg = readl_relaxed(ctrl_base_addr + DED_ERROR_INT); +- u32 val = 0; +- int i; +- +- for (i = 0; i < ARRAY_SIZE(ded_error_to_event); i++) +- val |= reg_to_event(reg, ded_error_to_event[i]); +- +- return val; +-} +- +-static u32 local_events(struct mc_pcie *port) +-{ +- void __iomem *bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; +- u32 reg = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL); +- u32 val = 0; +- int i; +- +- for (i = 0; i < ARRAY_SIZE(local_status_to_event); i++) +- val |= reg_to_event(reg, local_status_to_event[i]); +- +- return val; +-} +- +-static u32 get_events(struct mc_pcie *port) +-{ +- u32 events = 0; +- +- events |= pcie_events(port); +- events |= sec_errors(port); +- events |= ded_errors(port); +- events |= local_events(port); +- +- return events; +-} +- +-static irqreturn_t mc_event_handler(int irq, void *dev_id) +-{ +- struct mc_pcie *port = dev_id; +- struct device *dev = port->dev; +- struct irq_data *data; +- +- data = irq_domain_get_irq_data(port->event_domain, irq); +- +- if (event_cause[data->hwirq].str) +- dev_err_ratelimited(dev, "%s\n", event_cause[data->hwirq].str); +- else +- dev_err_ratelimited(dev, "bad event IRQ %ld\n", data->hwirq); +- +- return IRQ_HANDLED; +-} +- +-static void mc_handle_event(struct irq_desc *desc) +-{ +- struct mc_pcie *port = irq_desc_get_handler_data(desc); +- unsigned long events; +- u32 bit; +- struct irq_chip *chip = irq_desc_get_chip(desc); +- +- chained_irq_enter(chip, desc); +- +- events = get_events(port); +- +- for_each_set_bit(bit, &events, NUM_EVENTS) +- generic_handle_domain_irq(port->event_domain, bit); +- +- chained_irq_exit(chip, desc); +-} +- +-static void mc_ack_event_irq(struct irq_data *data) +-{ +- struct mc_pcie *port = irq_data_get_irq_chip_data(data); +- u32 event = data->hwirq; +- void __iomem *addr; +- u32 mask; +- +- addr = port->axi_base_addr + event_descs[event].base + +- event_descs[event].offset; +- mask = event_descs[event].mask; +- mask |= event_descs[event].enb_mask; +- +- writel_relaxed(mask, addr); +-} +- +-static void mc_mask_event_irq(struct irq_data *data) +-{ +- struct mc_pcie *port = irq_data_get_irq_chip_data(data); +- u32 event = data->hwirq; +- void __iomem *addr; +- u32 mask; +- u32 val; +- +- addr = port->axi_base_addr + event_descs[event].base + +- event_descs[event].mask_offset; +- mask = event_descs[event].mask; +- if (event_descs[event].enb_mask) { +- mask <<= PCIE_EVENT_INT_ENB_SHIFT; +- mask &= PCIE_EVENT_INT_ENB_MASK; +- } +- +- if (!event_descs[event].mask_high) +- mask = ~mask; +- +- raw_spin_lock(&port->lock); +- val = readl_relaxed(addr); +- if (event_descs[event].mask_high) +- val |= mask; +- else +- val &= mask; +- +- writel_relaxed(val, addr); +- raw_spin_unlock(&port->lock); +-} +- +-static void mc_unmask_event_irq(struct irq_data *data) +-{ +- struct mc_pcie *port = irq_data_get_irq_chip_data(data); +- u32 event = data->hwirq; +- void __iomem *addr; +- u32 mask; +- u32 val; +- +- addr = port->axi_base_addr + event_descs[event].base + +- event_descs[event].mask_offset; +- mask = event_descs[event].mask; +- +- if (event_descs[event].enb_mask) +- mask <<= PCIE_EVENT_INT_ENB_SHIFT; +- +- if (event_descs[event].mask_high) +- mask = ~mask; +- +- if (event_descs[event].enb_mask) +- mask &= PCIE_EVENT_INT_ENB_MASK; +- +- raw_spin_lock(&port->lock); +- val = readl_relaxed(addr); +- if (event_descs[event].mask_high) +- val &= mask; +- else +- val |= mask; +- writel_relaxed(val, addr); +- raw_spin_unlock(&port->lock); +-} +- +-static struct irq_chip mc_event_irq_chip = { +- .name = "Microchip PCIe EVENT", +- .irq_ack = mc_ack_event_irq, +- .irq_mask = mc_mask_event_irq, +- .irq_unmask = mc_unmask_event_irq, +-}; +- +-static int mc_pcie_event_map(struct irq_domain *domain, unsigned int irq, +- irq_hw_number_t hwirq) +-{ +- irq_set_chip_and_handler(irq, &mc_event_irq_chip, handle_level_irq); +- irq_set_chip_data(irq, domain->host_data); +- +- return 0; +-} +- +-static const struct irq_domain_ops event_domain_ops = { +- .map = mc_pcie_event_map, +-}; +- +-static inline void mc_pcie_deinit_clk(void *data) +-{ +- struct clk *clk = data; +- +- clk_disable_unprepare(clk); +-} +- +-static inline struct clk *mc_pcie_init_clk(struct device *dev, const char *id) +-{ +- struct clk *clk; +- int ret; +- +- clk = devm_clk_get_optional(dev, id); +- if (IS_ERR(clk)) +- return clk; +- if (!clk) +- return clk; +- +- ret = clk_prepare_enable(clk); +- if (ret) +- return ERR_PTR(ret); +- +- devm_add_action_or_reset(dev, mc_pcie_deinit_clk, clk); +- +- return clk; +-} +- +-static int mc_pcie_init_clks(struct device *dev) +-{ +- int i; +- struct clk *fic; +- +- /* +- * PCIe may be clocked via Fabric Interface using between 1 and 4 +- * clocks. Scan DT for clocks and enable them if present +- */ +- for (i = 0; i < ARRAY_SIZE(poss_clks); i++) { +- fic = mc_pcie_init_clk(dev, poss_clks[i]); +- if (IS_ERR(fic)) +- return PTR_ERR(fic); +- } +- +- return 0; +-} +- +-static int mc_pcie_init_irq_domains(struct mc_pcie *port) +-{ +- struct device *dev = port->dev; +- struct device_node *node = dev->of_node; +- struct device_node *pcie_intc_node; +- +- /* Setup INTx */ +- pcie_intc_node = of_get_next_child(node, NULL); +- if (!pcie_intc_node) { +- dev_err(dev, "failed to find PCIe Intc node\n"); +- return -EINVAL; +- } +- +- port->event_domain = irq_domain_add_linear(pcie_intc_node, NUM_EVENTS, +- &event_domain_ops, port); +- if (!port->event_domain) { +- dev_err(dev, "failed to get event domain\n"); +- of_node_put(pcie_intc_node); +- return -ENOMEM; +- } +- +- irq_domain_update_bus_token(port->event_domain, DOMAIN_BUS_NEXUS); +- +- port->intx_domain = irq_domain_add_linear(pcie_intc_node, PCI_NUM_INTX, +- &intx_domain_ops, port); +- if (!port->intx_domain) { +- dev_err(dev, "failed to get an INTx IRQ domain\n"); +- of_node_put(pcie_intc_node); +- return -ENOMEM; +- } +- +- irq_domain_update_bus_token(port->intx_domain, DOMAIN_BUS_WIRED); +- +- of_node_put(pcie_intc_node); +- raw_spin_lock_init(&port->lock); +- +- return mc_allocate_msi_domains(port); +-} +- +-static void mc_pcie_setup_window(void __iomem *bridge_base_addr, u32 index, +- phys_addr_t axi_addr, phys_addr_t pci_addr, +- size_t size) +-{ +- u32 atr_sz = ilog2(size) - 1; +- u32 val; +- +- if (index == 0) +- val = PCIE_CONFIG_INTERFACE; +- else +- val = PCIE_TX_RX_INTERFACE; +- +- writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + +- ATR0_AXI4_SLV0_TRSL_PARAM); +- +- val = lower_32_bits(axi_addr) | (atr_sz << ATR_SIZE_SHIFT) | +- ATR_IMPL_ENABLE; +- writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + +- ATR0_AXI4_SLV0_SRCADDR_PARAM); +- +- val = upper_32_bits(axi_addr); +- writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + +- ATR0_AXI4_SLV0_SRC_ADDR); +- +- val = lower_32_bits(pci_addr); +- writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + +- ATR0_AXI4_SLV0_TRSL_ADDR_LSB); +- +- val = upper_32_bits(pci_addr); +- writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + +- ATR0_AXI4_SLV0_TRSL_ADDR_UDW); +- +- val = readl(bridge_base_addr + ATR0_PCIE_WIN0_SRCADDR_PARAM); +- val |= (ATR0_PCIE_ATR_SIZE << ATR0_PCIE_ATR_SIZE_SHIFT); +- writel(val, bridge_base_addr + ATR0_PCIE_WIN0_SRCADDR_PARAM); +- writel(0, bridge_base_addr + ATR0_PCIE_WIN0_SRC_ADDR); +-} +- +-static int mc_pcie_setup_windows(struct platform_device *pdev, +- struct mc_pcie *port) +-{ +- void __iomem *bridge_base_addr = +- port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; +- struct pci_host_bridge *bridge = platform_get_drvdata(pdev); +- struct resource_entry *entry; +- u64 pci_addr; +- u32 index = 1; +- +- resource_list_for_each_entry(entry, &bridge->windows) { +- if (resource_type(entry->res) == IORESOURCE_MEM) { +- pci_addr = entry->res->start - entry->offset; +- mc_pcie_setup_window(bridge_base_addr, index, +- entry->res->start, pci_addr, +- resource_size(entry->res)); +- index++; +- } +- } +- +- return 0; +-} +- +-static inline void mc_clear_secs(struct mc_pcie *port) +-{ +- void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR; +- +- writel_relaxed(SEC_ERROR_INT_ALL_RAM_SEC_ERR_INT, ctrl_base_addr + +- SEC_ERROR_INT); +- writel_relaxed(0, ctrl_base_addr + SEC_ERROR_EVENT_CNT); +-} +- +-static inline void mc_clear_deds(struct mc_pcie *port) +-{ +- void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR; +- +- writel_relaxed(DED_ERROR_INT_ALL_RAM_DED_ERR_INT, ctrl_base_addr + +- DED_ERROR_INT); +- writel_relaxed(0, ctrl_base_addr + DED_ERROR_EVENT_CNT); +-} +- +-static void mc_disable_interrupts(struct mc_pcie *port) +-{ +- void __iomem *bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; +- void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR; +- u32 val; +- +- /* Ensure ECC bypass is enabled */ +- val = ECC_CONTROL_TX_RAM_ECC_BYPASS | +- ECC_CONTROL_RX_RAM_ECC_BYPASS | +- ECC_CONTROL_PCIE2AXI_RAM_ECC_BYPASS | +- ECC_CONTROL_AXI2PCIE_RAM_ECC_BYPASS; +- writel_relaxed(val, ctrl_base_addr + ECC_CONTROL); +- +- /* Disable SEC errors and clear any outstanding */ +- writel_relaxed(SEC_ERROR_INT_ALL_RAM_SEC_ERR_INT, ctrl_base_addr + +- SEC_ERROR_INT_MASK); +- mc_clear_secs(port); +- +- /* Disable DED errors and clear any outstanding */ +- writel_relaxed(DED_ERROR_INT_ALL_RAM_DED_ERR_INT, ctrl_base_addr + +- DED_ERROR_INT_MASK); +- mc_clear_deds(port); +- +- /* Disable local interrupts and clear any outstanding */ +- writel_relaxed(0, bridge_base_addr + IMASK_LOCAL); +- writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_LOCAL); +- writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_MSI); +- +- /* Disable PCIe events and clear any outstanding */ +- val = PCIE_EVENT_INT_L2_EXIT_INT | +- PCIE_EVENT_INT_HOTRST_EXIT_INT | +- PCIE_EVENT_INT_DLUP_EXIT_INT | +- PCIE_EVENT_INT_L2_EXIT_INT_MASK | +- PCIE_EVENT_INT_HOTRST_EXIT_INT_MASK | +- PCIE_EVENT_INT_DLUP_EXIT_INT_MASK; +- writel_relaxed(val, ctrl_base_addr + PCIE_EVENT_INT); +- +- /* Disable host interrupts and clear any outstanding */ +- writel_relaxed(0, bridge_base_addr + IMASK_HOST); +- writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_HOST); +-} +- +-static int mc_init_interrupts(struct platform_device *pdev, struct mc_pcie *port) +-{ +- struct device *dev = &pdev->dev; +- int irq; +- int i, intx_irq, msi_irq, event_irq; +- int ret; +- +- ret = mc_pcie_init_irq_domains(port); +- if (ret) { +- dev_err(dev, "failed creating IRQ domains\n"); +- return ret; +- } +- +- irq = platform_get_irq(pdev, 0); +- if (irq < 0) +- return -ENODEV; +- +- for (i = 0; i < NUM_EVENTS; i++) { +- event_irq = irq_create_mapping(port->event_domain, i); +- if (!event_irq) { +- dev_err(dev, "failed to map hwirq %d\n", i); +- return -ENXIO; +- } +- +- ret = devm_request_irq(dev, event_irq, mc_event_handler, +- 0, event_cause[i].sym, port); +- if (ret) { +- dev_err(dev, "failed to request IRQ %d\n", event_irq); +- return ret; +- } +- } +- +- intx_irq = irq_create_mapping(port->event_domain, +- EVENT_LOCAL_PM_MSI_INT_INTX); +- if (!intx_irq) { +- dev_err(dev, "failed to map INTx interrupt\n"); +- return -ENXIO; +- } +- +- /* Plug the INTx chained handler */ +- irq_set_chained_handler_and_data(intx_irq, mc_handle_intx, port); +- +- msi_irq = irq_create_mapping(port->event_domain, +- EVENT_LOCAL_PM_MSI_INT_MSI); +- if (!msi_irq) +- return -ENXIO; +- +- /* Plug the MSI chained handler */ +- irq_set_chained_handler_and_data(msi_irq, mc_handle_msi, port); +- +- /* Plug the main event chained handler */ +- irq_set_chained_handler_and_data(irq, mc_handle_event, port); +- +- return 0; +-} +- +-static int mc_platform_init(struct pci_config_window *cfg) +-{ +- struct device *dev = cfg->parent; +- struct platform_device *pdev = to_platform_device(dev); +- void __iomem *bridge_base_addr = +- port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; +- int ret; +- +- /* Configure address translation table 0 for PCIe config space */ +- mc_pcie_setup_window(bridge_base_addr, 0, cfg->res.start, +- cfg->res.start, +- resource_size(&cfg->res)); +- +- /* Need some fixups in config space */ +- mc_pcie_enable_msi(port, cfg->win); +- +- /* Configure non-config space outbound ranges */ +- ret = mc_pcie_setup_windows(pdev, port); +- if (ret) +- return ret; +- +- /* Address translation is up; safe to enable interrupts */ +- ret = mc_init_interrupts(pdev, port); +- if (ret) +- return ret; +- +- return 0; +-} +- +-static int mc_host_probe(struct platform_device *pdev) +-{ +- struct device *dev = &pdev->dev; +- void __iomem *bridge_base_addr; +- int ret; +- u32 val; +- +- port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL); +- if (!port) +- return -ENOMEM; +- +- port->dev = dev; +- +- port->axi_base_addr = devm_platform_ioremap_resource(pdev, 1); +- if (IS_ERR(port->axi_base_addr)) +- return PTR_ERR(port->axi_base_addr); +- +- mc_disable_interrupts(port); +- +- bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; +- +- /* Allow enabling MSI by disabling MSI-X */ +- val = readl(bridge_base_addr + PCIE_PCI_IRQ_DW0); +- val &= ~MSIX_CAP_MASK; +- writel(val, bridge_base_addr + PCIE_PCI_IRQ_DW0); +- +- /* Pick num vectors from bitfile programmed onto FPGA fabric */ +- val = readl(bridge_base_addr + PCIE_PCI_IRQ_DW0); +- val &= NUM_MSI_MSGS_MASK; +- val >>= NUM_MSI_MSGS_SHIFT; +- +- port->msi.num_vectors = 1 << val; +- +- /* Pick vector address from design */ +- port->msi.vector_phy = readl_relaxed(bridge_base_addr + IMSI_ADDR); +- +- ret = mc_pcie_init_clks(dev); +- if (ret) { +- dev_err(dev, "failed to get clock resources, error %d\n", ret); +- return -ENODEV; +- } +- +- return pci_host_common_probe(pdev); +-} +- +-static const struct pci_ecam_ops mc_ecam_ops = { +- .init = mc_platform_init, +- .pci_ops = { +- .map_bus = pci_ecam_map_bus, +- .read = pci_generic_config_read, +- .write = pci_generic_config_write, +- } +-}; +- +-static const struct of_device_id mc_pcie_of_match[] = { +- { +- .compatible = "microchip,pcie-host-1.0", +- .data = &mc_ecam_ops, +- }, +- {}, +-}; +- +-MODULE_DEVICE_TABLE(of, mc_pcie_of_match); +- +-static struct platform_driver mc_pcie_driver = { +- .probe = mc_host_probe, +- .driver = { +- .name = "microchip-pcie", +- .of_match_table = mc_pcie_of_match, +- .suppress_bind_attrs = true, +- }, +-}; +- +-builtin_platform_driver(mc_pcie_driver); +-MODULE_LICENSE("GPL"); +-MODULE_DESCRIPTION("Microchip PCIe host controller driver"); +-MODULE_AUTHOR("Daire McNamara "); +--- /dev/null ++++ b/drivers/pci/controller/plda/pcie-microchip-host.c +@@ -0,0 +1,1216 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Microchip AXI PCIe Bridge host controller driver ++ * ++ * Copyright (c) 2018 - 2020 Microchip Corporation. All rights reserved. ++ * ++ * Author: Daire McNamara ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "../../pci.h" ++ ++/* Number of MSI IRQs */ ++#define MC_MAX_NUM_MSI_IRQS 32 ++ ++/* PCIe Bridge Phy and Controller Phy offsets */ ++#define MC_PCIE1_BRIDGE_ADDR 0x00008000u ++#define MC_PCIE1_CTRL_ADDR 0x0000a000u ++ ++#define MC_PCIE_BRIDGE_ADDR (MC_PCIE1_BRIDGE_ADDR) ++#define MC_PCIE_CTRL_ADDR (MC_PCIE1_CTRL_ADDR) ++ ++/* PCIe Bridge Phy Regs */ ++#define PCIE_PCI_IRQ_DW0 0xa8 ++#define MSIX_CAP_MASK BIT(31) ++#define NUM_MSI_MSGS_MASK GENMASK(6, 4) ++#define NUM_MSI_MSGS_SHIFT 4 ++ ++#define IMASK_LOCAL 0x180 ++#define DMA_END_ENGINE_0_MASK 0x00000000u ++#define DMA_END_ENGINE_0_SHIFT 0 ++#define DMA_END_ENGINE_1_MASK 0x00000000u ++#define DMA_END_ENGINE_1_SHIFT 1 ++#define DMA_ERROR_ENGINE_0_MASK 0x00000100u ++#define DMA_ERROR_ENGINE_0_SHIFT 8 ++#define DMA_ERROR_ENGINE_1_MASK 0x00000200u ++#define DMA_ERROR_ENGINE_1_SHIFT 9 ++#define A_ATR_EVT_POST_ERR_MASK 0x00010000u ++#define A_ATR_EVT_POST_ERR_SHIFT 16 ++#define A_ATR_EVT_FETCH_ERR_MASK 0x00020000u ++#define A_ATR_EVT_FETCH_ERR_SHIFT 17 ++#define A_ATR_EVT_DISCARD_ERR_MASK 0x00040000u ++#define A_ATR_EVT_DISCARD_ERR_SHIFT 18 ++#define A_ATR_EVT_DOORBELL_MASK 0x00000000u ++#define A_ATR_EVT_DOORBELL_SHIFT 19 ++#define P_ATR_EVT_POST_ERR_MASK 0x00100000u ++#define P_ATR_EVT_POST_ERR_SHIFT 20 ++#define P_ATR_EVT_FETCH_ERR_MASK 0x00200000u ++#define P_ATR_EVT_FETCH_ERR_SHIFT 21 ++#define P_ATR_EVT_DISCARD_ERR_MASK 0x00400000u ++#define P_ATR_EVT_DISCARD_ERR_SHIFT 22 ++#define P_ATR_EVT_DOORBELL_MASK 0x00000000u ++#define P_ATR_EVT_DOORBELL_SHIFT 23 ++#define PM_MSI_INT_INTA_MASK 0x01000000u ++#define PM_MSI_INT_INTA_SHIFT 24 ++#define PM_MSI_INT_INTB_MASK 0x02000000u ++#define PM_MSI_INT_INTB_SHIFT 25 ++#define PM_MSI_INT_INTC_MASK 0x04000000u ++#define PM_MSI_INT_INTC_SHIFT 26 ++#define PM_MSI_INT_INTD_MASK 0x08000000u ++#define PM_MSI_INT_INTD_SHIFT 27 ++#define PM_MSI_INT_INTX_MASK 0x0f000000u ++#define PM_MSI_INT_INTX_SHIFT 24 ++#define PM_MSI_INT_MSI_MASK 0x10000000u ++#define PM_MSI_INT_MSI_SHIFT 28 ++#define PM_MSI_INT_AER_EVT_MASK 0x20000000u ++#define PM_MSI_INT_AER_EVT_SHIFT 29 ++#define PM_MSI_INT_EVENTS_MASK 0x40000000u ++#define PM_MSI_INT_EVENTS_SHIFT 30 ++#define PM_MSI_INT_SYS_ERR_MASK 0x80000000u ++#define PM_MSI_INT_SYS_ERR_SHIFT 31 ++#define NUM_LOCAL_EVENTS 15 ++#define ISTATUS_LOCAL 0x184 ++#define IMASK_HOST 0x188 ++#define ISTATUS_HOST 0x18c ++#define IMSI_ADDR 0x190 ++#define ISTATUS_MSI 0x194 ++ ++/* PCIe Master table init defines */ ++#define ATR0_PCIE_WIN0_SRCADDR_PARAM 0x600u ++#define ATR0_PCIE_ATR_SIZE 0x25 ++#define ATR0_PCIE_ATR_SIZE_SHIFT 1 ++#define ATR0_PCIE_WIN0_SRC_ADDR 0x604u ++#define ATR0_PCIE_WIN0_TRSL_ADDR_LSB 0x608u ++#define ATR0_PCIE_WIN0_TRSL_ADDR_UDW 0x60cu ++#define ATR0_PCIE_WIN0_TRSL_PARAM 0x610u ++ ++/* PCIe AXI slave table init defines */ ++#define ATR0_AXI4_SLV0_SRCADDR_PARAM 0x800u ++#define ATR_SIZE_SHIFT 1 ++#define ATR_IMPL_ENABLE 1 ++#define ATR0_AXI4_SLV0_SRC_ADDR 0x804u ++#define ATR0_AXI4_SLV0_TRSL_ADDR_LSB 0x808u ++#define ATR0_AXI4_SLV0_TRSL_ADDR_UDW 0x80cu ++#define ATR0_AXI4_SLV0_TRSL_PARAM 0x810u ++#define PCIE_TX_RX_INTERFACE 0x00000000u ++#define PCIE_CONFIG_INTERFACE 0x00000001u ++ ++#define ATR_ENTRY_SIZE 32 ++ ++/* PCIe Controller Phy Regs */ ++#define SEC_ERROR_EVENT_CNT 0x20 ++#define DED_ERROR_EVENT_CNT 0x24 ++#define SEC_ERROR_INT 0x28 ++#define SEC_ERROR_INT_TX_RAM_SEC_ERR_INT GENMASK(3, 0) ++#define SEC_ERROR_INT_RX_RAM_SEC_ERR_INT GENMASK(7, 4) ++#define SEC_ERROR_INT_PCIE2AXI_RAM_SEC_ERR_INT GENMASK(11, 8) ++#define SEC_ERROR_INT_AXI2PCIE_RAM_SEC_ERR_INT GENMASK(15, 12) ++#define SEC_ERROR_INT_ALL_RAM_SEC_ERR_INT GENMASK(15, 0) ++#define NUM_SEC_ERROR_INTS (4) ++#define SEC_ERROR_INT_MASK 0x2c ++#define DED_ERROR_INT 0x30 ++#define DED_ERROR_INT_TX_RAM_DED_ERR_INT GENMASK(3, 0) ++#define DED_ERROR_INT_RX_RAM_DED_ERR_INT GENMASK(7, 4) ++#define DED_ERROR_INT_PCIE2AXI_RAM_DED_ERR_INT GENMASK(11, 8) ++#define DED_ERROR_INT_AXI2PCIE_RAM_DED_ERR_INT GENMASK(15, 12) ++#define DED_ERROR_INT_ALL_RAM_DED_ERR_INT GENMASK(15, 0) ++#define NUM_DED_ERROR_INTS (4) ++#define DED_ERROR_INT_MASK 0x34 ++#define ECC_CONTROL 0x38 ++#define ECC_CONTROL_TX_RAM_INJ_ERROR_0 BIT(0) ++#define ECC_CONTROL_TX_RAM_INJ_ERROR_1 BIT(1) ++#define ECC_CONTROL_TX_RAM_INJ_ERROR_2 BIT(2) ++#define ECC_CONTROL_TX_RAM_INJ_ERROR_3 BIT(3) ++#define ECC_CONTROL_RX_RAM_INJ_ERROR_0 BIT(4) ++#define ECC_CONTROL_RX_RAM_INJ_ERROR_1 BIT(5) ++#define ECC_CONTROL_RX_RAM_INJ_ERROR_2 BIT(6) ++#define ECC_CONTROL_RX_RAM_INJ_ERROR_3 BIT(7) ++#define ECC_CONTROL_PCIE2AXI_RAM_INJ_ERROR_0 BIT(8) ++#define ECC_CONTROL_PCIE2AXI_RAM_INJ_ERROR_1 BIT(9) ++#define ECC_CONTROL_PCIE2AXI_RAM_INJ_ERROR_2 BIT(10) ++#define ECC_CONTROL_PCIE2AXI_RAM_INJ_ERROR_3 BIT(11) ++#define ECC_CONTROL_AXI2PCIE_RAM_INJ_ERROR_0 BIT(12) ++#define ECC_CONTROL_AXI2PCIE_RAM_INJ_ERROR_1 BIT(13) ++#define ECC_CONTROL_AXI2PCIE_RAM_INJ_ERROR_2 BIT(14) ++#define ECC_CONTROL_AXI2PCIE_RAM_INJ_ERROR_3 BIT(15) ++#define ECC_CONTROL_TX_RAM_ECC_BYPASS BIT(24) ++#define ECC_CONTROL_RX_RAM_ECC_BYPASS BIT(25) ++#define ECC_CONTROL_PCIE2AXI_RAM_ECC_BYPASS BIT(26) ++#define ECC_CONTROL_AXI2PCIE_RAM_ECC_BYPASS BIT(27) ++#define PCIE_EVENT_INT 0x14c ++#define PCIE_EVENT_INT_L2_EXIT_INT BIT(0) ++#define PCIE_EVENT_INT_HOTRST_EXIT_INT BIT(1) ++#define PCIE_EVENT_INT_DLUP_EXIT_INT BIT(2) ++#define PCIE_EVENT_INT_MASK GENMASK(2, 0) ++#define PCIE_EVENT_INT_L2_EXIT_INT_MASK BIT(16) ++#define PCIE_EVENT_INT_HOTRST_EXIT_INT_MASK BIT(17) ++#define PCIE_EVENT_INT_DLUP_EXIT_INT_MASK BIT(18) ++#define PCIE_EVENT_INT_ENB_MASK GENMASK(18, 16) ++#define PCIE_EVENT_INT_ENB_SHIFT 16 ++#define NUM_PCIE_EVENTS (3) ++ ++/* PCIe Config space MSI capability structure */ ++#define MC_MSI_CAP_CTRL_OFFSET 0xe0u ++ ++/* Events */ ++#define EVENT_PCIE_L2_EXIT 0 ++#define EVENT_PCIE_HOTRST_EXIT 1 ++#define EVENT_PCIE_DLUP_EXIT 2 ++#define EVENT_SEC_TX_RAM_SEC_ERR 3 ++#define EVENT_SEC_RX_RAM_SEC_ERR 4 ++#define EVENT_SEC_PCIE2AXI_RAM_SEC_ERR 5 ++#define EVENT_SEC_AXI2PCIE_RAM_SEC_ERR 6 ++#define EVENT_DED_TX_RAM_DED_ERR 7 ++#define EVENT_DED_RX_RAM_DED_ERR 8 ++#define EVENT_DED_PCIE2AXI_RAM_DED_ERR 9 ++#define EVENT_DED_AXI2PCIE_RAM_DED_ERR 10 ++#define EVENT_LOCAL_DMA_END_ENGINE_0 11 ++#define EVENT_LOCAL_DMA_END_ENGINE_1 12 ++#define EVENT_LOCAL_DMA_ERROR_ENGINE_0 13 ++#define EVENT_LOCAL_DMA_ERROR_ENGINE_1 14 ++#define EVENT_LOCAL_A_ATR_EVT_POST_ERR 15 ++#define EVENT_LOCAL_A_ATR_EVT_FETCH_ERR 16 ++#define EVENT_LOCAL_A_ATR_EVT_DISCARD_ERR 17 ++#define EVENT_LOCAL_A_ATR_EVT_DOORBELL 18 ++#define EVENT_LOCAL_P_ATR_EVT_POST_ERR 19 ++#define EVENT_LOCAL_P_ATR_EVT_FETCH_ERR 20 ++#define EVENT_LOCAL_P_ATR_EVT_DISCARD_ERR 21 ++#define EVENT_LOCAL_P_ATR_EVT_DOORBELL 22 ++#define EVENT_LOCAL_PM_MSI_INT_INTX 23 ++#define EVENT_LOCAL_PM_MSI_INT_MSI 24 ++#define EVENT_LOCAL_PM_MSI_INT_AER_EVT 25 ++#define EVENT_LOCAL_PM_MSI_INT_EVENTS 26 ++#define EVENT_LOCAL_PM_MSI_INT_SYS_ERR 27 ++#define NUM_EVENTS 28 ++ ++#define PCIE_EVENT_CAUSE(x, s) \ ++ [EVENT_PCIE_ ## x] = { __stringify(x), s } ++ ++#define SEC_ERROR_CAUSE(x, s) \ ++ [EVENT_SEC_ ## x] = { __stringify(x), s } ++ ++#define DED_ERROR_CAUSE(x, s) \ ++ [EVENT_DED_ ## x] = { __stringify(x), s } ++ ++#define LOCAL_EVENT_CAUSE(x, s) \ ++ [EVENT_LOCAL_ ## x] = { __stringify(x), s } ++ ++#define PCIE_EVENT(x) \ ++ .base = MC_PCIE_CTRL_ADDR, \ ++ .offset = PCIE_EVENT_INT, \ ++ .mask_offset = PCIE_EVENT_INT, \ ++ .mask_high = 1, \ ++ .mask = PCIE_EVENT_INT_ ## x ## _INT, \ ++ .enb_mask = PCIE_EVENT_INT_ENB_MASK ++ ++#define SEC_EVENT(x) \ ++ .base = MC_PCIE_CTRL_ADDR, \ ++ .offset = SEC_ERROR_INT, \ ++ .mask_offset = SEC_ERROR_INT_MASK, \ ++ .mask = SEC_ERROR_INT_ ## x ## _INT, \ ++ .mask_high = 1, \ ++ .enb_mask = 0 ++ ++#define DED_EVENT(x) \ ++ .base = MC_PCIE_CTRL_ADDR, \ ++ .offset = DED_ERROR_INT, \ ++ .mask_offset = DED_ERROR_INT_MASK, \ ++ .mask_high = 1, \ ++ .mask = DED_ERROR_INT_ ## x ## _INT, \ ++ .enb_mask = 0 ++ ++#define LOCAL_EVENT(x) \ ++ .base = MC_PCIE_BRIDGE_ADDR, \ ++ .offset = ISTATUS_LOCAL, \ ++ .mask_offset = IMASK_LOCAL, \ ++ .mask_high = 0, \ ++ .mask = x ## _MASK, \ ++ .enb_mask = 0 ++ ++#define PCIE_EVENT_TO_EVENT_MAP(x) \ ++ { PCIE_EVENT_INT_ ## x ## _INT, EVENT_PCIE_ ## x } ++ ++#define SEC_ERROR_TO_EVENT_MAP(x) \ ++ { SEC_ERROR_INT_ ## x ## _INT, EVENT_SEC_ ## x } ++ ++#define DED_ERROR_TO_EVENT_MAP(x) \ ++ { DED_ERROR_INT_ ## x ## _INT, EVENT_DED_ ## x } ++ ++#define LOCAL_STATUS_TO_EVENT_MAP(x) \ ++ { x ## _MASK, EVENT_LOCAL_ ## x } ++ ++struct event_map { ++ u32 reg_mask; ++ u32 event_bit; ++}; ++ ++struct mc_msi { ++ struct mutex lock; /* Protect used bitmap */ ++ struct irq_domain *msi_domain; ++ struct irq_domain *dev_domain; ++ u32 num_vectors; ++ u64 vector_phy; ++ DECLARE_BITMAP(used, MC_MAX_NUM_MSI_IRQS); ++}; ++ ++struct mc_pcie { ++ void __iomem *axi_base_addr; ++ struct device *dev; ++ struct irq_domain *intx_domain; ++ struct irq_domain *event_domain; ++ raw_spinlock_t lock; ++ struct mc_msi msi; ++}; ++ ++struct cause { ++ const char *sym; ++ const char *str; ++}; ++ ++static const struct cause event_cause[NUM_EVENTS] = { ++ PCIE_EVENT_CAUSE(L2_EXIT, "L2 exit event"), ++ PCIE_EVENT_CAUSE(HOTRST_EXIT, "Hot reset exit event"), ++ PCIE_EVENT_CAUSE(DLUP_EXIT, "DLUP exit event"), ++ SEC_ERROR_CAUSE(TX_RAM_SEC_ERR, "sec error in tx buffer"), ++ SEC_ERROR_CAUSE(RX_RAM_SEC_ERR, "sec error in rx buffer"), ++ SEC_ERROR_CAUSE(PCIE2AXI_RAM_SEC_ERR, "sec error in pcie2axi buffer"), ++ SEC_ERROR_CAUSE(AXI2PCIE_RAM_SEC_ERR, "sec error in axi2pcie buffer"), ++ DED_ERROR_CAUSE(TX_RAM_DED_ERR, "ded error in tx buffer"), ++ DED_ERROR_CAUSE(RX_RAM_DED_ERR, "ded error in rx buffer"), ++ DED_ERROR_CAUSE(PCIE2AXI_RAM_DED_ERR, "ded error in pcie2axi buffer"), ++ DED_ERROR_CAUSE(AXI2PCIE_RAM_DED_ERR, "ded error in axi2pcie buffer"), ++ LOCAL_EVENT_CAUSE(DMA_ERROR_ENGINE_0, "dma engine 0 error"), ++ LOCAL_EVENT_CAUSE(DMA_ERROR_ENGINE_1, "dma engine 1 error"), ++ LOCAL_EVENT_CAUSE(A_ATR_EVT_POST_ERR, "axi write request error"), ++ LOCAL_EVENT_CAUSE(A_ATR_EVT_FETCH_ERR, "axi read request error"), ++ LOCAL_EVENT_CAUSE(A_ATR_EVT_DISCARD_ERR, "axi read timeout"), ++ LOCAL_EVENT_CAUSE(P_ATR_EVT_POST_ERR, "pcie write request error"), ++ LOCAL_EVENT_CAUSE(P_ATR_EVT_FETCH_ERR, "pcie read request error"), ++ LOCAL_EVENT_CAUSE(P_ATR_EVT_DISCARD_ERR, "pcie read timeout"), ++ LOCAL_EVENT_CAUSE(PM_MSI_INT_AER_EVT, "aer event"), ++ LOCAL_EVENT_CAUSE(PM_MSI_INT_EVENTS, "pm/ltr/hotplug event"), ++ LOCAL_EVENT_CAUSE(PM_MSI_INT_SYS_ERR, "system error"), ++}; ++ ++static struct event_map pcie_event_to_event[] = { ++ PCIE_EVENT_TO_EVENT_MAP(L2_EXIT), ++ PCIE_EVENT_TO_EVENT_MAP(HOTRST_EXIT), ++ PCIE_EVENT_TO_EVENT_MAP(DLUP_EXIT), ++}; ++ ++static struct event_map sec_error_to_event[] = { ++ SEC_ERROR_TO_EVENT_MAP(TX_RAM_SEC_ERR), ++ SEC_ERROR_TO_EVENT_MAP(RX_RAM_SEC_ERR), ++ SEC_ERROR_TO_EVENT_MAP(PCIE2AXI_RAM_SEC_ERR), ++ SEC_ERROR_TO_EVENT_MAP(AXI2PCIE_RAM_SEC_ERR), ++}; ++ ++static struct event_map ded_error_to_event[] = { ++ DED_ERROR_TO_EVENT_MAP(TX_RAM_DED_ERR), ++ DED_ERROR_TO_EVENT_MAP(RX_RAM_DED_ERR), ++ DED_ERROR_TO_EVENT_MAP(PCIE2AXI_RAM_DED_ERR), ++ DED_ERROR_TO_EVENT_MAP(AXI2PCIE_RAM_DED_ERR), ++}; ++ ++static struct event_map local_status_to_event[] = { ++ LOCAL_STATUS_TO_EVENT_MAP(DMA_END_ENGINE_0), ++ LOCAL_STATUS_TO_EVENT_MAP(DMA_END_ENGINE_1), ++ LOCAL_STATUS_TO_EVENT_MAP(DMA_ERROR_ENGINE_0), ++ LOCAL_STATUS_TO_EVENT_MAP(DMA_ERROR_ENGINE_1), ++ LOCAL_STATUS_TO_EVENT_MAP(A_ATR_EVT_POST_ERR), ++ LOCAL_STATUS_TO_EVENT_MAP(A_ATR_EVT_FETCH_ERR), ++ LOCAL_STATUS_TO_EVENT_MAP(A_ATR_EVT_DISCARD_ERR), ++ LOCAL_STATUS_TO_EVENT_MAP(A_ATR_EVT_DOORBELL), ++ LOCAL_STATUS_TO_EVENT_MAP(P_ATR_EVT_POST_ERR), ++ LOCAL_STATUS_TO_EVENT_MAP(P_ATR_EVT_FETCH_ERR), ++ LOCAL_STATUS_TO_EVENT_MAP(P_ATR_EVT_DISCARD_ERR), ++ LOCAL_STATUS_TO_EVENT_MAP(P_ATR_EVT_DOORBELL), ++ LOCAL_STATUS_TO_EVENT_MAP(PM_MSI_INT_INTX), ++ LOCAL_STATUS_TO_EVENT_MAP(PM_MSI_INT_MSI), ++ LOCAL_STATUS_TO_EVENT_MAP(PM_MSI_INT_AER_EVT), ++ LOCAL_STATUS_TO_EVENT_MAP(PM_MSI_INT_EVENTS), ++ LOCAL_STATUS_TO_EVENT_MAP(PM_MSI_INT_SYS_ERR), ++}; ++ ++static struct { ++ u32 base; ++ u32 offset; ++ u32 mask; ++ u32 shift; ++ u32 enb_mask; ++ u32 mask_high; ++ u32 mask_offset; ++} event_descs[] = { ++ { PCIE_EVENT(L2_EXIT) }, ++ { PCIE_EVENT(HOTRST_EXIT) }, ++ { PCIE_EVENT(DLUP_EXIT) }, ++ { SEC_EVENT(TX_RAM_SEC_ERR) }, ++ { SEC_EVENT(RX_RAM_SEC_ERR) }, ++ { SEC_EVENT(PCIE2AXI_RAM_SEC_ERR) }, ++ { SEC_EVENT(AXI2PCIE_RAM_SEC_ERR) }, ++ { DED_EVENT(TX_RAM_DED_ERR) }, ++ { DED_EVENT(RX_RAM_DED_ERR) }, ++ { DED_EVENT(PCIE2AXI_RAM_DED_ERR) }, ++ { DED_EVENT(AXI2PCIE_RAM_DED_ERR) }, ++ { LOCAL_EVENT(DMA_END_ENGINE_0) }, ++ { LOCAL_EVENT(DMA_END_ENGINE_1) }, ++ { LOCAL_EVENT(DMA_ERROR_ENGINE_0) }, ++ { LOCAL_EVENT(DMA_ERROR_ENGINE_1) }, ++ { LOCAL_EVENT(A_ATR_EVT_POST_ERR) }, ++ { LOCAL_EVENT(A_ATR_EVT_FETCH_ERR) }, ++ { LOCAL_EVENT(A_ATR_EVT_DISCARD_ERR) }, ++ { LOCAL_EVENT(A_ATR_EVT_DOORBELL) }, ++ { LOCAL_EVENT(P_ATR_EVT_POST_ERR) }, ++ { LOCAL_EVENT(P_ATR_EVT_FETCH_ERR) }, ++ { LOCAL_EVENT(P_ATR_EVT_DISCARD_ERR) }, ++ { LOCAL_EVENT(P_ATR_EVT_DOORBELL) }, ++ { LOCAL_EVENT(PM_MSI_INT_INTX) }, ++ { LOCAL_EVENT(PM_MSI_INT_MSI) }, ++ { LOCAL_EVENT(PM_MSI_INT_AER_EVT) }, ++ { LOCAL_EVENT(PM_MSI_INT_EVENTS) }, ++ { LOCAL_EVENT(PM_MSI_INT_SYS_ERR) }, ++}; ++ ++static char poss_clks[][5] = { "fic0", "fic1", "fic2", "fic3" }; ++ ++static struct mc_pcie *port; ++ ++static void mc_pcie_enable_msi(struct mc_pcie *port, void __iomem *ecam) ++{ ++ struct mc_msi *msi = &port->msi; ++ u16 reg; ++ u8 queue_size; ++ ++ /* Fixup MSI enable flag */ ++ reg = readw_relaxed(ecam + MC_MSI_CAP_CTRL_OFFSET + PCI_MSI_FLAGS); ++ reg |= PCI_MSI_FLAGS_ENABLE; ++ writew_relaxed(reg, ecam + MC_MSI_CAP_CTRL_OFFSET + PCI_MSI_FLAGS); ++ ++ /* Fixup PCI MSI queue flags */ ++ queue_size = FIELD_GET(PCI_MSI_FLAGS_QMASK, reg); ++ reg |= FIELD_PREP(PCI_MSI_FLAGS_QSIZE, queue_size); ++ writew_relaxed(reg, ecam + MC_MSI_CAP_CTRL_OFFSET + PCI_MSI_FLAGS); ++ ++ /* Fixup MSI addr fields */ ++ writel_relaxed(lower_32_bits(msi->vector_phy), ++ ecam + MC_MSI_CAP_CTRL_OFFSET + PCI_MSI_ADDRESS_LO); ++ writel_relaxed(upper_32_bits(msi->vector_phy), ++ ecam + MC_MSI_CAP_CTRL_OFFSET + PCI_MSI_ADDRESS_HI); ++} ++ ++static void mc_handle_msi(struct irq_desc *desc) ++{ ++ struct mc_pcie *port = irq_desc_get_handler_data(desc); ++ struct irq_chip *chip = irq_desc_get_chip(desc); ++ struct device *dev = port->dev; ++ struct mc_msi *msi = &port->msi; ++ void __iomem *bridge_base_addr = ++ port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; ++ unsigned long status; ++ u32 bit; ++ int ret; ++ ++ chained_irq_enter(chip, desc); ++ ++ status = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL); ++ if (status & PM_MSI_INT_MSI_MASK) { ++ writel_relaxed(status & PM_MSI_INT_MSI_MASK, bridge_base_addr + ISTATUS_LOCAL); ++ status = readl_relaxed(bridge_base_addr + ISTATUS_MSI); ++ for_each_set_bit(bit, &status, msi->num_vectors) { ++ ret = generic_handle_domain_irq(msi->dev_domain, bit); ++ if (ret) ++ dev_err_ratelimited(dev, "bad MSI IRQ %d\n", ++ bit); ++ } ++ } ++ ++ chained_irq_exit(chip, desc); ++} ++ ++static void mc_msi_bottom_irq_ack(struct irq_data *data) ++{ ++ struct mc_pcie *port = irq_data_get_irq_chip_data(data); ++ void __iomem *bridge_base_addr = ++ port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; ++ u32 bitpos = data->hwirq; ++ ++ writel_relaxed(BIT(bitpos), bridge_base_addr + ISTATUS_MSI); ++} ++ ++static void mc_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) ++{ ++ struct mc_pcie *port = irq_data_get_irq_chip_data(data); ++ phys_addr_t addr = port->msi.vector_phy; ++ ++ msg->address_lo = lower_32_bits(addr); ++ msg->address_hi = upper_32_bits(addr); ++ msg->data = data->hwirq; ++ ++ dev_dbg(port->dev, "msi#%x address_hi %#x address_lo %#x\n", ++ (int)data->hwirq, msg->address_hi, msg->address_lo); ++} ++ ++static int mc_msi_set_affinity(struct irq_data *irq_data, ++ const struct cpumask *mask, bool force) ++{ ++ return -EINVAL; ++} ++ ++static struct irq_chip mc_msi_bottom_irq_chip = { ++ .name = "Microchip MSI", ++ .irq_ack = mc_msi_bottom_irq_ack, ++ .irq_compose_msi_msg = mc_compose_msi_msg, ++ .irq_set_affinity = mc_msi_set_affinity, ++}; ++ ++static int mc_irq_msi_domain_alloc(struct irq_domain *domain, unsigned int virq, ++ unsigned int nr_irqs, void *args) ++{ ++ struct mc_pcie *port = domain->host_data; ++ struct mc_msi *msi = &port->msi; ++ unsigned long bit; ++ ++ mutex_lock(&msi->lock); ++ bit = find_first_zero_bit(msi->used, msi->num_vectors); ++ if (bit >= msi->num_vectors) { ++ mutex_unlock(&msi->lock); ++ return -ENOSPC; ++ } ++ ++ set_bit(bit, msi->used); ++ ++ irq_domain_set_info(domain, virq, bit, &mc_msi_bottom_irq_chip, ++ domain->host_data, handle_edge_irq, NULL, NULL); ++ ++ mutex_unlock(&msi->lock); ++ ++ return 0; ++} ++ ++static void mc_irq_msi_domain_free(struct irq_domain *domain, unsigned int virq, ++ unsigned int nr_irqs) ++{ ++ struct irq_data *d = irq_domain_get_irq_data(domain, virq); ++ struct mc_pcie *port = irq_data_get_irq_chip_data(d); ++ struct mc_msi *msi = &port->msi; ++ ++ mutex_lock(&msi->lock); ++ ++ if (test_bit(d->hwirq, msi->used)) ++ __clear_bit(d->hwirq, msi->used); ++ else ++ dev_err(port->dev, "trying to free unused MSI%lu\n", d->hwirq); ++ ++ mutex_unlock(&msi->lock); ++} ++ ++static const struct irq_domain_ops msi_domain_ops = { ++ .alloc = mc_irq_msi_domain_alloc, ++ .free = mc_irq_msi_domain_free, ++}; ++ ++static struct irq_chip mc_msi_irq_chip = { ++ .name = "Microchip PCIe MSI", ++ .irq_ack = irq_chip_ack_parent, ++ .irq_mask = pci_msi_mask_irq, ++ .irq_unmask = pci_msi_unmask_irq, ++}; ++ ++static struct msi_domain_info mc_msi_domain_info = { ++ .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | ++ MSI_FLAG_PCI_MSIX), ++ .chip = &mc_msi_irq_chip, ++}; ++ ++static int mc_allocate_msi_domains(struct mc_pcie *port) ++{ ++ struct device *dev = port->dev; ++ struct fwnode_handle *fwnode = of_node_to_fwnode(dev->of_node); ++ struct mc_msi *msi = &port->msi; ++ ++ mutex_init(&port->msi.lock); ++ ++ msi->dev_domain = irq_domain_add_linear(NULL, msi->num_vectors, ++ &msi_domain_ops, port); ++ if (!msi->dev_domain) { ++ dev_err(dev, "failed to create IRQ domain\n"); ++ return -ENOMEM; ++ } ++ ++ msi->msi_domain = pci_msi_create_irq_domain(fwnode, &mc_msi_domain_info, ++ msi->dev_domain); ++ if (!msi->msi_domain) { ++ dev_err(dev, "failed to create MSI domain\n"); ++ irq_domain_remove(msi->dev_domain); ++ return -ENOMEM; ++ } ++ ++ return 0; ++} ++ ++static void mc_handle_intx(struct irq_desc *desc) ++{ ++ struct mc_pcie *port = irq_desc_get_handler_data(desc); ++ struct irq_chip *chip = irq_desc_get_chip(desc); ++ struct device *dev = port->dev; ++ void __iomem *bridge_base_addr = ++ port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; ++ unsigned long status; ++ u32 bit; ++ int ret; ++ ++ chained_irq_enter(chip, desc); ++ ++ status = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL); ++ if (status & PM_MSI_INT_INTX_MASK) { ++ status &= PM_MSI_INT_INTX_MASK; ++ status >>= PM_MSI_INT_INTX_SHIFT; ++ for_each_set_bit(bit, &status, PCI_NUM_INTX) { ++ ret = generic_handle_domain_irq(port->intx_domain, bit); ++ if (ret) ++ dev_err_ratelimited(dev, "bad INTx IRQ %d\n", ++ bit); ++ } ++ } ++ ++ chained_irq_exit(chip, desc); ++} ++ ++static void mc_ack_intx_irq(struct irq_data *data) ++{ ++ struct mc_pcie *port = irq_data_get_irq_chip_data(data); ++ void __iomem *bridge_base_addr = ++ port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; ++ u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT); ++ ++ writel_relaxed(mask, bridge_base_addr + ISTATUS_LOCAL); ++} ++ ++static void mc_mask_intx_irq(struct irq_data *data) ++{ ++ struct mc_pcie *port = irq_data_get_irq_chip_data(data); ++ void __iomem *bridge_base_addr = ++ port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; ++ unsigned long flags; ++ u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT); ++ u32 val; ++ ++ raw_spin_lock_irqsave(&port->lock, flags); ++ val = readl_relaxed(bridge_base_addr + IMASK_LOCAL); ++ val &= ~mask; ++ writel_relaxed(val, bridge_base_addr + IMASK_LOCAL); ++ raw_spin_unlock_irqrestore(&port->lock, flags); ++} ++ ++static void mc_unmask_intx_irq(struct irq_data *data) ++{ ++ struct mc_pcie *port = irq_data_get_irq_chip_data(data); ++ void __iomem *bridge_base_addr = ++ port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; ++ unsigned long flags; ++ u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT); ++ u32 val; ++ ++ raw_spin_lock_irqsave(&port->lock, flags); ++ val = readl_relaxed(bridge_base_addr + IMASK_LOCAL); ++ val |= mask; ++ writel_relaxed(val, bridge_base_addr + IMASK_LOCAL); ++ raw_spin_unlock_irqrestore(&port->lock, flags); ++} ++ ++static struct irq_chip mc_intx_irq_chip = { ++ .name = "Microchip PCIe INTx", ++ .irq_ack = mc_ack_intx_irq, ++ .irq_mask = mc_mask_intx_irq, ++ .irq_unmask = mc_unmask_intx_irq, ++}; ++ ++static int mc_pcie_intx_map(struct irq_domain *domain, unsigned int irq, ++ irq_hw_number_t hwirq) ++{ ++ irq_set_chip_and_handler(irq, &mc_intx_irq_chip, handle_level_irq); ++ irq_set_chip_data(irq, domain->host_data); ++ ++ return 0; ++} ++ ++static const struct irq_domain_ops intx_domain_ops = { ++ .map = mc_pcie_intx_map, ++}; ++ ++static inline u32 reg_to_event(u32 reg, struct event_map field) ++{ ++ return (reg & field.reg_mask) ? BIT(field.event_bit) : 0; ++} ++ ++static u32 pcie_events(struct mc_pcie *port) ++{ ++ void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR; ++ u32 reg = readl_relaxed(ctrl_base_addr + PCIE_EVENT_INT); ++ u32 val = 0; ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(pcie_event_to_event); i++) ++ val |= reg_to_event(reg, pcie_event_to_event[i]); ++ ++ return val; ++} ++ ++static u32 sec_errors(struct mc_pcie *port) ++{ ++ void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR; ++ u32 reg = readl_relaxed(ctrl_base_addr + SEC_ERROR_INT); ++ u32 val = 0; ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(sec_error_to_event); i++) ++ val |= reg_to_event(reg, sec_error_to_event[i]); ++ ++ return val; ++} ++ ++static u32 ded_errors(struct mc_pcie *port) ++{ ++ void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR; ++ u32 reg = readl_relaxed(ctrl_base_addr + DED_ERROR_INT); ++ u32 val = 0; ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(ded_error_to_event); i++) ++ val |= reg_to_event(reg, ded_error_to_event[i]); ++ ++ return val; ++} ++ ++static u32 local_events(struct mc_pcie *port) ++{ ++ void __iomem *bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; ++ u32 reg = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL); ++ u32 val = 0; ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(local_status_to_event); i++) ++ val |= reg_to_event(reg, local_status_to_event[i]); ++ ++ return val; ++} ++ ++static u32 get_events(struct mc_pcie *port) ++{ ++ u32 events = 0; ++ ++ events |= pcie_events(port); ++ events |= sec_errors(port); ++ events |= ded_errors(port); ++ events |= local_events(port); ++ ++ return events; ++} ++ ++static irqreturn_t mc_event_handler(int irq, void *dev_id) ++{ ++ struct mc_pcie *port = dev_id; ++ struct device *dev = port->dev; ++ struct irq_data *data; ++ ++ data = irq_domain_get_irq_data(port->event_domain, irq); ++ ++ if (event_cause[data->hwirq].str) ++ dev_err_ratelimited(dev, "%s\n", event_cause[data->hwirq].str); ++ else ++ dev_err_ratelimited(dev, "bad event IRQ %ld\n", data->hwirq); ++ ++ return IRQ_HANDLED; ++} ++ ++static void mc_handle_event(struct irq_desc *desc) ++{ ++ struct mc_pcie *port = irq_desc_get_handler_data(desc); ++ unsigned long events; ++ u32 bit; ++ struct irq_chip *chip = irq_desc_get_chip(desc); ++ ++ chained_irq_enter(chip, desc); ++ ++ events = get_events(port); ++ ++ for_each_set_bit(bit, &events, NUM_EVENTS) ++ generic_handle_domain_irq(port->event_domain, bit); ++ ++ chained_irq_exit(chip, desc); ++} ++ ++static void mc_ack_event_irq(struct irq_data *data) ++{ ++ struct mc_pcie *port = irq_data_get_irq_chip_data(data); ++ u32 event = data->hwirq; ++ void __iomem *addr; ++ u32 mask; ++ ++ addr = port->axi_base_addr + event_descs[event].base + ++ event_descs[event].offset; ++ mask = event_descs[event].mask; ++ mask |= event_descs[event].enb_mask; ++ ++ writel_relaxed(mask, addr); ++} ++ ++static void mc_mask_event_irq(struct irq_data *data) ++{ ++ struct mc_pcie *port = irq_data_get_irq_chip_data(data); ++ u32 event = data->hwirq; ++ void __iomem *addr; ++ u32 mask; ++ u32 val; ++ ++ addr = port->axi_base_addr + event_descs[event].base + ++ event_descs[event].mask_offset; ++ mask = event_descs[event].mask; ++ if (event_descs[event].enb_mask) { ++ mask <<= PCIE_EVENT_INT_ENB_SHIFT; ++ mask &= PCIE_EVENT_INT_ENB_MASK; ++ } ++ ++ if (!event_descs[event].mask_high) ++ mask = ~mask; ++ ++ raw_spin_lock(&port->lock); ++ val = readl_relaxed(addr); ++ if (event_descs[event].mask_high) ++ val |= mask; ++ else ++ val &= mask; ++ ++ writel_relaxed(val, addr); ++ raw_spin_unlock(&port->lock); ++} ++ ++static void mc_unmask_event_irq(struct irq_data *data) ++{ ++ struct mc_pcie *port = irq_data_get_irq_chip_data(data); ++ u32 event = data->hwirq; ++ void __iomem *addr; ++ u32 mask; ++ u32 val; ++ ++ addr = port->axi_base_addr + event_descs[event].base + ++ event_descs[event].mask_offset; ++ mask = event_descs[event].mask; ++ ++ if (event_descs[event].enb_mask) ++ mask <<= PCIE_EVENT_INT_ENB_SHIFT; ++ ++ if (event_descs[event].mask_high) ++ mask = ~mask; ++ ++ if (event_descs[event].enb_mask) ++ mask &= PCIE_EVENT_INT_ENB_MASK; ++ ++ raw_spin_lock(&port->lock); ++ val = readl_relaxed(addr); ++ if (event_descs[event].mask_high) ++ val &= mask; ++ else ++ val |= mask; ++ writel_relaxed(val, addr); ++ raw_spin_unlock(&port->lock); ++} ++ ++static struct irq_chip mc_event_irq_chip = { ++ .name = "Microchip PCIe EVENT", ++ .irq_ack = mc_ack_event_irq, ++ .irq_mask = mc_mask_event_irq, ++ .irq_unmask = mc_unmask_event_irq, ++}; ++ ++static int mc_pcie_event_map(struct irq_domain *domain, unsigned int irq, ++ irq_hw_number_t hwirq) ++{ ++ irq_set_chip_and_handler(irq, &mc_event_irq_chip, handle_level_irq); ++ irq_set_chip_data(irq, domain->host_data); ++ ++ return 0; ++} ++ ++static const struct irq_domain_ops event_domain_ops = { ++ .map = mc_pcie_event_map, ++}; ++ ++static inline void mc_pcie_deinit_clk(void *data) ++{ ++ struct clk *clk = data; ++ ++ clk_disable_unprepare(clk); ++} ++ ++static inline struct clk *mc_pcie_init_clk(struct device *dev, const char *id) ++{ ++ struct clk *clk; ++ int ret; ++ ++ clk = devm_clk_get_optional(dev, id); ++ if (IS_ERR(clk)) ++ return clk; ++ if (!clk) ++ return clk; ++ ++ ret = clk_prepare_enable(clk); ++ if (ret) ++ return ERR_PTR(ret); ++ ++ devm_add_action_or_reset(dev, mc_pcie_deinit_clk, clk); ++ ++ return clk; ++} ++ ++static int mc_pcie_init_clks(struct device *dev) ++{ ++ int i; ++ struct clk *fic; ++ ++ /* ++ * PCIe may be clocked via Fabric Interface using between 1 and 4 ++ * clocks. Scan DT for clocks and enable them if present ++ */ ++ for (i = 0; i < ARRAY_SIZE(poss_clks); i++) { ++ fic = mc_pcie_init_clk(dev, poss_clks[i]); ++ if (IS_ERR(fic)) ++ return PTR_ERR(fic); ++ } ++ ++ return 0; ++} ++ ++static int mc_pcie_init_irq_domains(struct mc_pcie *port) ++{ ++ struct device *dev = port->dev; ++ struct device_node *node = dev->of_node; ++ struct device_node *pcie_intc_node; ++ ++ /* Setup INTx */ ++ pcie_intc_node = of_get_next_child(node, NULL); ++ if (!pcie_intc_node) { ++ dev_err(dev, "failed to find PCIe Intc node\n"); ++ return -EINVAL; ++ } ++ ++ port->event_domain = irq_domain_add_linear(pcie_intc_node, NUM_EVENTS, ++ &event_domain_ops, port); ++ if (!port->event_domain) { ++ dev_err(dev, "failed to get event domain\n"); ++ of_node_put(pcie_intc_node); ++ return -ENOMEM; ++ } ++ ++ irq_domain_update_bus_token(port->event_domain, DOMAIN_BUS_NEXUS); ++ ++ port->intx_domain = irq_domain_add_linear(pcie_intc_node, PCI_NUM_INTX, ++ &intx_domain_ops, port); ++ if (!port->intx_domain) { ++ dev_err(dev, "failed to get an INTx IRQ domain\n"); ++ of_node_put(pcie_intc_node); ++ return -ENOMEM; ++ } ++ ++ irq_domain_update_bus_token(port->intx_domain, DOMAIN_BUS_WIRED); ++ ++ of_node_put(pcie_intc_node); ++ raw_spin_lock_init(&port->lock); ++ ++ return mc_allocate_msi_domains(port); ++} ++ ++static void mc_pcie_setup_window(void __iomem *bridge_base_addr, u32 index, ++ phys_addr_t axi_addr, phys_addr_t pci_addr, ++ size_t size) ++{ ++ u32 atr_sz = ilog2(size) - 1; ++ u32 val; ++ ++ if (index == 0) ++ val = PCIE_CONFIG_INTERFACE; ++ else ++ val = PCIE_TX_RX_INTERFACE; ++ ++ writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + ++ ATR0_AXI4_SLV0_TRSL_PARAM); ++ ++ val = lower_32_bits(axi_addr) | (atr_sz << ATR_SIZE_SHIFT) | ++ ATR_IMPL_ENABLE; ++ writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + ++ ATR0_AXI4_SLV0_SRCADDR_PARAM); ++ ++ val = upper_32_bits(axi_addr); ++ writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + ++ ATR0_AXI4_SLV0_SRC_ADDR); ++ ++ val = lower_32_bits(pci_addr); ++ writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + ++ ATR0_AXI4_SLV0_TRSL_ADDR_LSB); ++ ++ val = upper_32_bits(pci_addr); ++ writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + ++ ATR0_AXI4_SLV0_TRSL_ADDR_UDW); ++ ++ val = readl(bridge_base_addr + ATR0_PCIE_WIN0_SRCADDR_PARAM); ++ val |= (ATR0_PCIE_ATR_SIZE << ATR0_PCIE_ATR_SIZE_SHIFT); ++ writel(val, bridge_base_addr + ATR0_PCIE_WIN0_SRCADDR_PARAM); ++ writel(0, bridge_base_addr + ATR0_PCIE_WIN0_SRC_ADDR); ++} ++ ++static int mc_pcie_setup_windows(struct platform_device *pdev, ++ struct mc_pcie *port) ++{ ++ void __iomem *bridge_base_addr = ++ port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; ++ struct pci_host_bridge *bridge = platform_get_drvdata(pdev); ++ struct resource_entry *entry; ++ u64 pci_addr; ++ u32 index = 1; ++ ++ resource_list_for_each_entry(entry, &bridge->windows) { ++ if (resource_type(entry->res) == IORESOURCE_MEM) { ++ pci_addr = entry->res->start - entry->offset; ++ mc_pcie_setup_window(bridge_base_addr, index, ++ entry->res->start, pci_addr, ++ resource_size(entry->res)); ++ index++; ++ } ++ } ++ ++ return 0; ++} ++ ++static inline void mc_clear_secs(struct mc_pcie *port) ++{ ++ void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR; ++ ++ writel_relaxed(SEC_ERROR_INT_ALL_RAM_SEC_ERR_INT, ctrl_base_addr + ++ SEC_ERROR_INT); ++ writel_relaxed(0, ctrl_base_addr + SEC_ERROR_EVENT_CNT); ++} ++ ++static inline void mc_clear_deds(struct mc_pcie *port) ++{ ++ void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR; ++ ++ writel_relaxed(DED_ERROR_INT_ALL_RAM_DED_ERR_INT, ctrl_base_addr + ++ DED_ERROR_INT); ++ writel_relaxed(0, ctrl_base_addr + DED_ERROR_EVENT_CNT); ++} ++ ++static void mc_disable_interrupts(struct mc_pcie *port) ++{ ++ void __iomem *bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; ++ void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR; ++ u32 val; ++ ++ /* Ensure ECC bypass is enabled */ ++ val = ECC_CONTROL_TX_RAM_ECC_BYPASS | ++ ECC_CONTROL_RX_RAM_ECC_BYPASS | ++ ECC_CONTROL_PCIE2AXI_RAM_ECC_BYPASS | ++ ECC_CONTROL_AXI2PCIE_RAM_ECC_BYPASS; ++ writel_relaxed(val, ctrl_base_addr + ECC_CONTROL); ++ ++ /* Disable SEC errors and clear any outstanding */ ++ writel_relaxed(SEC_ERROR_INT_ALL_RAM_SEC_ERR_INT, ctrl_base_addr + ++ SEC_ERROR_INT_MASK); ++ mc_clear_secs(port); ++ ++ /* Disable DED errors and clear any outstanding */ ++ writel_relaxed(DED_ERROR_INT_ALL_RAM_DED_ERR_INT, ctrl_base_addr + ++ DED_ERROR_INT_MASK); ++ mc_clear_deds(port); ++ ++ /* Disable local interrupts and clear any outstanding */ ++ writel_relaxed(0, bridge_base_addr + IMASK_LOCAL); ++ writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_LOCAL); ++ writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_MSI); ++ ++ /* Disable PCIe events and clear any outstanding */ ++ val = PCIE_EVENT_INT_L2_EXIT_INT | ++ PCIE_EVENT_INT_HOTRST_EXIT_INT | ++ PCIE_EVENT_INT_DLUP_EXIT_INT | ++ PCIE_EVENT_INT_L2_EXIT_INT_MASK | ++ PCIE_EVENT_INT_HOTRST_EXIT_INT_MASK | ++ PCIE_EVENT_INT_DLUP_EXIT_INT_MASK; ++ writel_relaxed(val, ctrl_base_addr + PCIE_EVENT_INT); ++ ++ /* Disable host interrupts and clear any outstanding */ ++ writel_relaxed(0, bridge_base_addr + IMASK_HOST); ++ writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_HOST); ++} ++ ++static int mc_init_interrupts(struct platform_device *pdev, struct mc_pcie *port) ++{ ++ struct device *dev = &pdev->dev; ++ int irq; ++ int i, intx_irq, msi_irq, event_irq; ++ int ret; ++ ++ ret = mc_pcie_init_irq_domains(port); ++ if (ret) { ++ dev_err(dev, "failed creating IRQ domains\n"); ++ return ret; ++ } ++ ++ irq = platform_get_irq(pdev, 0); ++ if (irq < 0) ++ return -ENODEV; ++ ++ for (i = 0; i < NUM_EVENTS; i++) { ++ event_irq = irq_create_mapping(port->event_domain, i); ++ if (!event_irq) { ++ dev_err(dev, "failed to map hwirq %d\n", i); ++ return -ENXIO; ++ } ++ ++ ret = devm_request_irq(dev, event_irq, mc_event_handler, ++ 0, event_cause[i].sym, port); ++ if (ret) { ++ dev_err(dev, "failed to request IRQ %d\n", event_irq); ++ return ret; ++ } ++ } ++ ++ intx_irq = irq_create_mapping(port->event_domain, ++ EVENT_LOCAL_PM_MSI_INT_INTX); ++ if (!intx_irq) { ++ dev_err(dev, "failed to map INTx interrupt\n"); ++ return -ENXIO; ++ } ++ ++ /* Plug the INTx chained handler */ ++ irq_set_chained_handler_and_data(intx_irq, mc_handle_intx, port); ++ ++ msi_irq = irq_create_mapping(port->event_domain, ++ EVENT_LOCAL_PM_MSI_INT_MSI); ++ if (!msi_irq) ++ return -ENXIO; ++ ++ /* Plug the MSI chained handler */ ++ irq_set_chained_handler_and_data(msi_irq, mc_handle_msi, port); ++ ++ /* Plug the main event chained handler */ ++ irq_set_chained_handler_and_data(irq, mc_handle_event, port); ++ ++ return 0; ++} ++ ++static int mc_platform_init(struct pci_config_window *cfg) ++{ ++ struct device *dev = cfg->parent; ++ struct platform_device *pdev = to_platform_device(dev); ++ void __iomem *bridge_base_addr = ++ port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; ++ int ret; ++ ++ /* Configure address translation table 0 for PCIe config space */ ++ mc_pcie_setup_window(bridge_base_addr, 0, cfg->res.start, ++ cfg->res.start, ++ resource_size(&cfg->res)); ++ ++ /* Need some fixups in config space */ ++ mc_pcie_enable_msi(port, cfg->win); ++ ++ /* Configure non-config space outbound ranges */ ++ ret = mc_pcie_setup_windows(pdev, port); ++ if (ret) ++ return ret; ++ ++ /* Address translation is up; safe to enable interrupts */ ++ ret = mc_init_interrupts(pdev, port); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++static int mc_host_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ void __iomem *bridge_base_addr; ++ int ret; ++ u32 val; ++ ++ port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL); ++ if (!port) ++ return -ENOMEM; ++ ++ port->dev = dev; ++ ++ port->axi_base_addr = devm_platform_ioremap_resource(pdev, 1); ++ if (IS_ERR(port->axi_base_addr)) ++ return PTR_ERR(port->axi_base_addr); ++ ++ mc_disable_interrupts(port); ++ ++ bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; ++ ++ /* Allow enabling MSI by disabling MSI-X */ ++ val = readl(bridge_base_addr + PCIE_PCI_IRQ_DW0); ++ val &= ~MSIX_CAP_MASK; ++ writel(val, bridge_base_addr + PCIE_PCI_IRQ_DW0); ++ ++ /* Pick num vectors from bitfile programmed onto FPGA fabric */ ++ val = readl(bridge_base_addr + PCIE_PCI_IRQ_DW0); ++ val &= NUM_MSI_MSGS_MASK; ++ val >>= NUM_MSI_MSGS_SHIFT; ++ ++ port->msi.num_vectors = 1 << val; ++ ++ /* Pick vector address from design */ ++ port->msi.vector_phy = readl_relaxed(bridge_base_addr + IMSI_ADDR); ++ ++ ret = mc_pcie_init_clks(dev); ++ if (ret) { ++ dev_err(dev, "failed to get clock resources, error %d\n", ret); ++ return -ENODEV; ++ } ++ ++ return pci_host_common_probe(pdev); ++} ++ ++static const struct pci_ecam_ops mc_ecam_ops = { ++ .init = mc_platform_init, ++ .pci_ops = { ++ .map_bus = pci_ecam_map_bus, ++ .read = pci_generic_config_read, ++ .write = pci_generic_config_write, ++ } ++}; ++ ++static const struct of_device_id mc_pcie_of_match[] = { ++ { ++ .compatible = "microchip,pcie-host-1.0", ++ .data = &mc_ecam_ops, ++ }, ++ {}, ++}; ++ ++MODULE_DEVICE_TABLE(of, mc_pcie_of_match); ++ ++static struct platform_driver mc_pcie_driver = { ++ .probe = mc_host_probe, ++ .driver = { ++ .name = "microchip-pcie", ++ .of_match_table = mc_pcie_of_match, ++ .suppress_bind_attrs = true, ++ }, ++}; ++ ++builtin_platform_driver(mc_pcie_driver); ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("Microchip PCIe host controller driver"); ++MODULE_AUTHOR("Daire McNamara "); diff --git a/target/linux/starfive/patches-6.6/0016-PCI-microchip-Move-PLDA-IP-register-macros-to-pcie-p.patch b/target/linux/starfive/patches-6.6/0016-PCI-microchip-Move-PLDA-IP-register-macros-to-pcie-p.patch new file mode 100644 index 0000000000..0aacec2769 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0016-PCI-microchip-Move-PLDA-IP-register-macros-to-pcie-p.patch @@ -0,0 +1,259 @@ +From eca1b864bb2d4e8d9811506979560a89351c2e37 Mon Sep 17 00:00:00 2001 +From: Minda Chen +Date: Mon, 8 Jan 2024 19:05:53 +0800 +Subject: [PATCH 016/116] PCI: microchip: Move PLDA IP register macros to + pcie-plda.h + +Move PLDA PCIe host controller IP registers macros to pcie-plda.h, +including bridge registers and local IRQ event number. + +Signed-off-by: Minda Chen +Reviewed-by: Conor Dooley +--- + .../pci/controller/plda/pcie-microchip-host.c | 108 +++--------------- + drivers/pci/controller/plda/pcie-plda.h | 108 ++++++++++++++++++ + 2 files changed, 124 insertions(+), 92 deletions(-) + create mode 100644 drivers/pci/controller/plda/pcie-plda.h + +--- a/drivers/pci/controller/plda/pcie-microchip-host.c ++++ b/drivers/pci/controller/plda/pcie-microchip-host.c +@@ -19,6 +19,7 @@ + #include + + #include "../../pci.h" ++#include "pcie-plda.h" + + /* Number of MSI IRQs */ + #define MC_MAX_NUM_MSI_IRQS 32 +@@ -30,84 +31,6 @@ + #define MC_PCIE_BRIDGE_ADDR (MC_PCIE1_BRIDGE_ADDR) + #define MC_PCIE_CTRL_ADDR (MC_PCIE1_CTRL_ADDR) + +-/* PCIe Bridge Phy Regs */ +-#define PCIE_PCI_IRQ_DW0 0xa8 +-#define MSIX_CAP_MASK BIT(31) +-#define NUM_MSI_MSGS_MASK GENMASK(6, 4) +-#define NUM_MSI_MSGS_SHIFT 4 +- +-#define IMASK_LOCAL 0x180 +-#define DMA_END_ENGINE_0_MASK 0x00000000u +-#define DMA_END_ENGINE_0_SHIFT 0 +-#define DMA_END_ENGINE_1_MASK 0x00000000u +-#define DMA_END_ENGINE_1_SHIFT 1 +-#define DMA_ERROR_ENGINE_0_MASK 0x00000100u +-#define DMA_ERROR_ENGINE_0_SHIFT 8 +-#define DMA_ERROR_ENGINE_1_MASK 0x00000200u +-#define DMA_ERROR_ENGINE_1_SHIFT 9 +-#define A_ATR_EVT_POST_ERR_MASK 0x00010000u +-#define A_ATR_EVT_POST_ERR_SHIFT 16 +-#define A_ATR_EVT_FETCH_ERR_MASK 0x00020000u +-#define A_ATR_EVT_FETCH_ERR_SHIFT 17 +-#define A_ATR_EVT_DISCARD_ERR_MASK 0x00040000u +-#define A_ATR_EVT_DISCARD_ERR_SHIFT 18 +-#define A_ATR_EVT_DOORBELL_MASK 0x00000000u +-#define A_ATR_EVT_DOORBELL_SHIFT 19 +-#define P_ATR_EVT_POST_ERR_MASK 0x00100000u +-#define P_ATR_EVT_POST_ERR_SHIFT 20 +-#define P_ATR_EVT_FETCH_ERR_MASK 0x00200000u +-#define P_ATR_EVT_FETCH_ERR_SHIFT 21 +-#define P_ATR_EVT_DISCARD_ERR_MASK 0x00400000u +-#define P_ATR_EVT_DISCARD_ERR_SHIFT 22 +-#define P_ATR_EVT_DOORBELL_MASK 0x00000000u +-#define P_ATR_EVT_DOORBELL_SHIFT 23 +-#define PM_MSI_INT_INTA_MASK 0x01000000u +-#define PM_MSI_INT_INTA_SHIFT 24 +-#define PM_MSI_INT_INTB_MASK 0x02000000u +-#define PM_MSI_INT_INTB_SHIFT 25 +-#define PM_MSI_INT_INTC_MASK 0x04000000u +-#define PM_MSI_INT_INTC_SHIFT 26 +-#define PM_MSI_INT_INTD_MASK 0x08000000u +-#define PM_MSI_INT_INTD_SHIFT 27 +-#define PM_MSI_INT_INTX_MASK 0x0f000000u +-#define PM_MSI_INT_INTX_SHIFT 24 +-#define PM_MSI_INT_MSI_MASK 0x10000000u +-#define PM_MSI_INT_MSI_SHIFT 28 +-#define PM_MSI_INT_AER_EVT_MASK 0x20000000u +-#define PM_MSI_INT_AER_EVT_SHIFT 29 +-#define PM_MSI_INT_EVENTS_MASK 0x40000000u +-#define PM_MSI_INT_EVENTS_SHIFT 30 +-#define PM_MSI_INT_SYS_ERR_MASK 0x80000000u +-#define PM_MSI_INT_SYS_ERR_SHIFT 31 +-#define NUM_LOCAL_EVENTS 15 +-#define ISTATUS_LOCAL 0x184 +-#define IMASK_HOST 0x188 +-#define ISTATUS_HOST 0x18c +-#define IMSI_ADDR 0x190 +-#define ISTATUS_MSI 0x194 +- +-/* PCIe Master table init defines */ +-#define ATR0_PCIE_WIN0_SRCADDR_PARAM 0x600u +-#define ATR0_PCIE_ATR_SIZE 0x25 +-#define ATR0_PCIE_ATR_SIZE_SHIFT 1 +-#define ATR0_PCIE_WIN0_SRC_ADDR 0x604u +-#define ATR0_PCIE_WIN0_TRSL_ADDR_LSB 0x608u +-#define ATR0_PCIE_WIN0_TRSL_ADDR_UDW 0x60cu +-#define ATR0_PCIE_WIN0_TRSL_PARAM 0x610u +- +-/* PCIe AXI slave table init defines */ +-#define ATR0_AXI4_SLV0_SRCADDR_PARAM 0x800u +-#define ATR_SIZE_SHIFT 1 +-#define ATR_IMPL_ENABLE 1 +-#define ATR0_AXI4_SLV0_SRC_ADDR 0x804u +-#define ATR0_AXI4_SLV0_TRSL_ADDR_LSB 0x808u +-#define ATR0_AXI4_SLV0_TRSL_ADDR_UDW 0x80cu +-#define ATR0_AXI4_SLV0_TRSL_PARAM 0x810u +-#define PCIE_TX_RX_INTERFACE 0x00000000u +-#define PCIE_CONFIG_INTERFACE 0x00000001u +- +-#define ATR_ENTRY_SIZE 32 +- + /* PCIe Controller Phy Regs */ + #define SEC_ERROR_EVENT_CNT 0x20 + #define DED_ERROR_EVENT_CNT 0x24 +@@ -179,20 +102,21 @@ + #define EVENT_LOCAL_DMA_END_ENGINE_1 12 + #define EVENT_LOCAL_DMA_ERROR_ENGINE_0 13 + #define EVENT_LOCAL_DMA_ERROR_ENGINE_1 14 +-#define EVENT_LOCAL_A_ATR_EVT_POST_ERR 15 +-#define EVENT_LOCAL_A_ATR_EVT_FETCH_ERR 16 +-#define EVENT_LOCAL_A_ATR_EVT_DISCARD_ERR 17 +-#define EVENT_LOCAL_A_ATR_EVT_DOORBELL 18 +-#define EVENT_LOCAL_P_ATR_EVT_POST_ERR 19 +-#define EVENT_LOCAL_P_ATR_EVT_FETCH_ERR 20 +-#define EVENT_LOCAL_P_ATR_EVT_DISCARD_ERR 21 +-#define EVENT_LOCAL_P_ATR_EVT_DOORBELL 22 +-#define EVENT_LOCAL_PM_MSI_INT_INTX 23 +-#define EVENT_LOCAL_PM_MSI_INT_MSI 24 +-#define EVENT_LOCAL_PM_MSI_INT_AER_EVT 25 +-#define EVENT_LOCAL_PM_MSI_INT_EVENTS 26 +-#define EVENT_LOCAL_PM_MSI_INT_SYS_ERR 27 +-#define NUM_EVENTS 28 ++#define NUM_MC_EVENTS 15 ++#define EVENT_LOCAL_A_ATR_EVT_POST_ERR (NUM_MC_EVENTS + PLDA_AXI_POST_ERR) ++#define EVENT_LOCAL_A_ATR_EVT_FETCH_ERR (NUM_MC_EVENTS + PLDA_AXI_FETCH_ERR) ++#define EVENT_LOCAL_A_ATR_EVT_DISCARD_ERR (NUM_MC_EVENTS + PLDA_AXI_DISCARD_ERR) ++#define EVENT_LOCAL_A_ATR_EVT_DOORBELL (NUM_MC_EVENTS + PLDA_AXI_DOORBELL) ++#define EVENT_LOCAL_P_ATR_EVT_POST_ERR (NUM_MC_EVENTS + PLDA_PCIE_POST_ERR) ++#define EVENT_LOCAL_P_ATR_EVT_FETCH_ERR (NUM_MC_EVENTS + PLDA_PCIE_FETCH_ERR) ++#define EVENT_LOCAL_P_ATR_EVT_DISCARD_ERR (NUM_MC_EVENTS + PLDA_PCIE_DISCARD_ERR) ++#define EVENT_LOCAL_P_ATR_EVT_DOORBELL (NUM_MC_EVENTS + PLDA_PCIE_DOORBELL) ++#define EVENT_LOCAL_PM_MSI_INT_INTX (NUM_MC_EVENTS + PLDA_INTX) ++#define EVENT_LOCAL_PM_MSI_INT_MSI (NUM_MC_EVENTS + PLDA_MSI) ++#define EVENT_LOCAL_PM_MSI_INT_AER_EVT (NUM_MC_EVENTS + PLDA_AER_EVENT) ++#define EVENT_LOCAL_PM_MSI_INT_EVENTS (NUM_MC_EVENTS + PLDA_MISC_EVENTS) ++#define EVENT_LOCAL_PM_MSI_INT_SYS_ERR (NUM_MC_EVENTS + PLDA_SYS_ERR) ++#define NUM_EVENTS (NUM_MC_EVENTS + PLDA_INT_EVENT_NUM) + + #define PCIE_EVENT_CAUSE(x, s) \ + [EVENT_PCIE_ ## x] = { __stringify(x), s } +--- /dev/null ++++ b/drivers/pci/controller/plda/pcie-plda.h +@@ -0,0 +1,108 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * PLDA PCIe host controller driver ++ */ ++ ++#ifndef _PCIE_PLDA_H ++#define _PCIE_PLDA_H ++ ++/* PCIe Bridge Phy Regs */ ++#define PCIE_PCI_IRQ_DW0 0xa8 ++#define MSIX_CAP_MASK BIT(31) ++#define NUM_MSI_MSGS_MASK GENMASK(6, 4) ++#define NUM_MSI_MSGS_SHIFT 4 ++ ++#define IMASK_LOCAL 0x180 ++#define DMA_END_ENGINE_0_MASK 0x00000000u ++#define DMA_END_ENGINE_0_SHIFT 0 ++#define DMA_END_ENGINE_1_MASK 0x00000000u ++#define DMA_END_ENGINE_1_SHIFT 1 ++#define DMA_ERROR_ENGINE_0_MASK 0x00000100u ++#define DMA_ERROR_ENGINE_0_SHIFT 8 ++#define DMA_ERROR_ENGINE_1_MASK 0x00000200u ++#define DMA_ERROR_ENGINE_1_SHIFT 9 ++#define A_ATR_EVT_POST_ERR_MASK 0x00010000u ++#define A_ATR_EVT_POST_ERR_SHIFT 16 ++#define A_ATR_EVT_FETCH_ERR_MASK 0x00020000u ++#define A_ATR_EVT_FETCH_ERR_SHIFT 17 ++#define A_ATR_EVT_DISCARD_ERR_MASK 0x00040000u ++#define A_ATR_EVT_DISCARD_ERR_SHIFT 18 ++#define A_ATR_EVT_DOORBELL_MASK 0x00000000u ++#define A_ATR_EVT_DOORBELL_SHIFT 19 ++#define P_ATR_EVT_POST_ERR_MASK 0x00100000u ++#define P_ATR_EVT_POST_ERR_SHIFT 20 ++#define P_ATR_EVT_FETCH_ERR_MASK 0x00200000u ++#define P_ATR_EVT_FETCH_ERR_SHIFT 21 ++#define P_ATR_EVT_DISCARD_ERR_MASK 0x00400000u ++#define P_ATR_EVT_DISCARD_ERR_SHIFT 22 ++#define P_ATR_EVT_DOORBELL_MASK 0x00000000u ++#define P_ATR_EVT_DOORBELL_SHIFT 23 ++#define PM_MSI_INT_INTA_MASK 0x01000000u ++#define PM_MSI_INT_INTA_SHIFT 24 ++#define PM_MSI_INT_INTB_MASK 0x02000000u ++#define PM_MSI_INT_INTB_SHIFT 25 ++#define PM_MSI_INT_INTC_MASK 0x04000000u ++#define PM_MSI_INT_INTC_SHIFT 26 ++#define PM_MSI_INT_INTD_MASK 0x08000000u ++#define PM_MSI_INT_INTD_SHIFT 27 ++#define PM_MSI_INT_INTX_MASK 0x0f000000u ++#define PM_MSI_INT_INTX_SHIFT 24 ++#define PM_MSI_INT_MSI_MASK 0x10000000u ++#define PM_MSI_INT_MSI_SHIFT 28 ++#define PM_MSI_INT_AER_EVT_MASK 0x20000000u ++#define PM_MSI_INT_AER_EVT_SHIFT 29 ++#define PM_MSI_INT_EVENTS_MASK 0x40000000u ++#define PM_MSI_INT_EVENTS_SHIFT 30 ++#define PM_MSI_INT_SYS_ERR_MASK 0x80000000u ++#define PM_MSI_INT_SYS_ERR_SHIFT 31 ++#define NUM_LOCAL_EVENTS 15 ++#define ISTATUS_LOCAL 0x184 ++#define IMASK_HOST 0x188 ++#define ISTATUS_HOST 0x18c ++#define IMSI_ADDR 0x190 ++#define ISTATUS_MSI 0x194 ++ ++/* PCIe Master table init defines */ ++#define ATR0_PCIE_WIN0_SRCADDR_PARAM 0x600u ++#define ATR0_PCIE_ATR_SIZE 0x25 ++#define ATR0_PCIE_ATR_SIZE_SHIFT 1 ++#define ATR0_PCIE_WIN0_SRC_ADDR 0x604u ++#define ATR0_PCIE_WIN0_TRSL_ADDR_LSB 0x608u ++#define ATR0_PCIE_WIN0_TRSL_ADDR_UDW 0x60cu ++#define ATR0_PCIE_WIN0_TRSL_PARAM 0x610u ++ ++/* PCIe AXI slave table init defines */ ++#define ATR0_AXI4_SLV0_SRCADDR_PARAM 0x800u ++#define ATR_SIZE_SHIFT 1 ++#define ATR_IMPL_ENABLE 1 ++#define ATR0_AXI4_SLV0_SRC_ADDR 0x804u ++#define ATR0_AXI4_SLV0_TRSL_ADDR_LSB 0x808u ++#define ATR0_AXI4_SLV0_TRSL_ADDR_UDW 0x80cu ++#define ATR0_AXI4_SLV0_TRSL_PARAM 0x810u ++#define PCIE_TX_RX_INTERFACE 0x00000000u ++#define PCIE_CONFIG_INTERFACE 0x00000001u ++ ++#define ATR_ENTRY_SIZE 32 ++ ++enum plda_int_event { ++ PLDA_AXI_POST_ERR, ++ PLDA_AXI_FETCH_ERR, ++ PLDA_AXI_DISCARD_ERR, ++ PLDA_AXI_DOORBELL, ++ PLDA_PCIE_POST_ERR, ++ PLDA_PCIE_FETCH_ERR, ++ PLDA_PCIE_DISCARD_ERR, ++ PLDA_PCIE_DOORBELL, ++ PLDA_INTX, ++ PLDA_MSI, ++ PLDA_AER_EVENT, ++ PLDA_MISC_EVENTS, ++ PLDA_SYS_ERR, ++ PLDA_INT_EVENT_NUM ++}; ++ ++#define PLDA_NUM_DMA_EVENTS 16 ++ ++#define PLDA_MAX_INT_NUM (PLDA_NUM_DMA_EVENTS + PLDA_INT_EVENT_NUM) ++ ++#endif diff --git a/target/linux/starfive/patches-6.6/0017-PCI-microchip-Add-bridge_addr-field-to-struct-mc_pci.patch b/target/linux/starfive/patches-6.6/0017-PCI-microchip-Add-bridge_addr-field-to-struct-mc_pci.patch new file mode 100644 index 0000000000..bc3f909116 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0017-PCI-microchip-Add-bridge_addr-field-to-struct-mc_pci.patch @@ -0,0 +1,107 @@ +From 6ee4d4568314425079ae88229bb9abbff9b92b8b Mon Sep 17 00:00:00 2001 +From: Minda Chen +Date: Mon, 8 Jan 2024 19:05:54 +0800 +Subject: [PATCH 017/116] PCI: microchip: Add bridge_addr field to struct + mc_pcie + +For bridge address base is common PLDA field, Add this to struct mc_pcie +first. + +INTx and MSI codes interrupts codes will get the bridge base address from +port->bridge_addr. These codes will be changed to common codes. +axi_base_addr is Microchip its own data. + +Signed-off-by: Minda Chen +Reviewed-by: Conor Dooley +--- + .../pci/controller/plda/pcie-microchip-host.c | 23 ++++++++----------- + 1 file changed, 9 insertions(+), 14 deletions(-) + +--- a/drivers/pci/controller/plda/pcie-microchip-host.c ++++ b/drivers/pci/controller/plda/pcie-microchip-host.c +@@ -195,6 +195,7 @@ struct mc_pcie { + struct irq_domain *event_domain; + raw_spinlock_t lock; + struct mc_msi msi; ++ void __iomem *bridge_addr; + }; + + struct cause { +@@ -339,8 +340,7 @@ static void mc_handle_msi(struct irq_des + struct irq_chip *chip = irq_desc_get_chip(desc); + struct device *dev = port->dev; + struct mc_msi *msi = &port->msi; +- void __iomem *bridge_base_addr = +- port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; ++ void __iomem *bridge_base_addr = port->bridge_addr; + unsigned long status; + u32 bit; + int ret; +@@ -365,8 +365,7 @@ static void mc_handle_msi(struct irq_des + static void mc_msi_bottom_irq_ack(struct irq_data *data) + { + struct mc_pcie *port = irq_data_get_irq_chip_data(data); +- void __iomem *bridge_base_addr = +- port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; ++ void __iomem *bridge_base_addr = port->bridge_addr; + u32 bitpos = data->hwirq; + + writel_relaxed(BIT(bitpos), bridge_base_addr + ISTATUS_MSI); +@@ -488,8 +487,7 @@ static void mc_handle_intx(struct irq_de + struct mc_pcie *port = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); + struct device *dev = port->dev; +- void __iomem *bridge_base_addr = +- port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; ++ void __iomem *bridge_base_addr = port->bridge_addr; + unsigned long status; + u32 bit; + int ret; +@@ -514,8 +512,7 @@ static void mc_handle_intx(struct irq_de + static void mc_ack_intx_irq(struct irq_data *data) + { + struct mc_pcie *port = irq_data_get_irq_chip_data(data); +- void __iomem *bridge_base_addr = +- port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; ++ void __iomem *bridge_base_addr = port->bridge_addr; + u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT); + + writel_relaxed(mask, bridge_base_addr + ISTATUS_LOCAL); +@@ -524,8 +521,7 @@ static void mc_ack_intx_irq(struct irq_d + static void mc_mask_intx_irq(struct irq_data *data) + { + struct mc_pcie *port = irq_data_get_irq_chip_data(data); +- void __iomem *bridge_base_addr = +- port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; ++ void __iomem *bridge_base_addr = port->bridge_addr; + unsigned long flags; + u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT); + u32 val; +@@ -540,8 +536,7 @@ static void mc_mask_intx_irq(struct irq_ + static void mc_unmask_intx_irq(struct irq_data *data) + { + struct mc_pcie *port = irq_data_get_irq_chip_data(data); +- void __iomem *bridge_base_addr = +- port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; ++ void __iomem *bridge_base_addr = port->bridge_addr; + unsigned long flags; + u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT); + u32 val; +@@ -896,8 +891,7 @@ static void mc_pcie_setup_window(void __ + static int mc_pcie_setup_windows(struct platform_device *pdev, + struct mc_pcie *port) + { +- void __iomem *bridge_base_addr = +- port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; ++ void __iomem *bridge_base_addr = port->bridge_addr; + struct pci_host_bridge *bridge = platform_get_drvdata(pdev); + struct resource_entry *entry; + u64 pci_addr; +@@ -1081,6 +1075,7 @@ static int mc_host_probe(struct platform + mc_disable_interrupts(port); + + bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; ++ port->bridge_addr = bridge_base_addr; + + /* Allow enabling MSI by disabling MSI-X */ + val = readl(bridge_base_addr + PCIE_PCI_IRQ_DW0); diff --git a/target/linux/starfive/patches-6.6/0018-PCI-microchip-Rename-two-PCIe-data-structures.patch b/target/linux/starfive/patches-6.6/0018-PCI-microchip-Rename-two-PCIe-data-structures.patch new file mode 100644 index 0000000000..a9e7b8975b --- /dev/null +++ b/target/linux/starfive/patches-6.6/0018-PCI-microchip-Rename-two-PCIe-data-structures.patch @@ -0,0 +1,347 @@ +From 7c1c679bdd0b6b727248edbee77836024c935f91 Mon Sep 17 00:00:00 2001 +From: Minda Chen +Date: Mon, 8 Jan 2024 19:05:55 +0800 +Subject: [PATCH 018/116] PCI: microchip: Rename two PCIe data structures + +Add PLDA PCIe related data structures by rename data structure name from +mc_* to plda_*. + +axi_base_addr is stayed in struct mc_pcie for it's microchip its own data. + +The event interrupt codes is still using struct mc_pcie because the event +interrupt codes can not be re-used. + +Signed-off-by: Minda Chen +Reviewed-by: Conor Dooley +--- + .../pci/controller/plda/pcie-microchip-host.c | 96 ++++++++++--------- + 1 file changed, 53 insertions(+), 43 deletions(-) + +--- a/drivers/pci/controller/plda/pcie-microchip-host.c ++++ b/drivers/pci/controller/plda/pcie-microchip-host.c +@@ -22,7 +22,7 @@ + #include "pcie-plda.h" + + /* Number of MSI IRQs */ +-#define MC_MAX_NUM_MSI_IRQS 32 ++#define PLDA_MAX_NUM_MSI_IRQS 32 + + /* PCIe Bridge Phy and Controller Phy offsets */ + #define MC_PCIE1_BRIDGE_ADDR 0x00008000u +@@ -179,25 +179,29 @@ struct event_map { + u32 event_bit; + }; + +-struct mc_msi { ++struct plda_msi { + struct mutex lock; /* Protect used bitmap */ + struct irq_domain *msi_domain; + struct irq_domain *dev_domain; + u32 num_vectors; + u64 vector_phy; +- DECLARE_BITMAP(used, MC_MAX_NUM_MSI_IRQS); ++ DECLARE_BITMAP(used, PLDA_MAX_NUM_MSI_IRQS); + }; + +-struct mc_pcie { +- void __iomem *axi_base_addr; ++struct plda_pcie_rp { + struct device *dev; + struct irq_domain *intx_domain; + struct irq_domain *event_domain; + raw_spinlock_t lock; +- struct mc_msi msi; ++ struct plda_msi msi; + void __iomem *bridge_addr; + }; + ++struct mc_pcie { ++ struct plda_pcie_rp plda; ++ void __iomem *axi_base_addr; ++}; ++ + struct cause { + const char *sym; + const char *str; +@@ -313,7 +317,7 @@ static struct mc_pcie *port; + + static void mc_pcie_enable_msi(struct mc_pcie *port, void __iomem *ecam) + { +- struct mc_msi *msi = &port->msi; ++ struct plda_msi *msi = &port->plda.msi; + u16 reg; + u8 queue_size; + +@@ -336,10 +340,10 @@ static void mc_pcie_enable_msi(struct mc + + static void mc_handle_msi(struct irq_desc *desc) + { +- struct mc_pcie *port = irq_desc_get_handler_data(desc); ++ struct plda_pcie_rp *port = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); + struct device *dev = port->dev; +- struct mc_msi *msi = &port->msi; ++ struct plda_msi *msi = &port->msi; + void __iomem *bridge_base_addr = port->bridge_addr; + unsigned long status; + u32 bit; +@@ -364,7 +368,7 @@ static void mc_handle_msi(struct irq_des + + static void mc_msi_bottom_irq_ack(struct irq_data *data) + { +- struct mc_pcie *port = irq_data_get_irq_chip_data(data); ++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); + void __iomem *bridge_base_addr = port->bridge_addr; + u32 bitpos = data->hwirq; + +@@ -373,7 +377,7 @@ static void mc_msi_bottom_irq_ack(struct + + static void mc_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) + { +- struct mc_pcie *port = irq_data_get_irq_chip_data(data); ++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); + phys_addr_t addr = port->msi.vector_phy; + + msg->address_lo = lower_32_bits(addr); +@@ -400,8 +404,8 @@ static struct irq_chip mc_msi_bottom_irq + static int mc_irq_msi_domain_alloc(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs, void *args) + { +- struct mc_pcie *port = domain->host_data; +- struct mc_msi *msi = &port->msi; ++ struct plda_pcie_rp *port = domain->host_data; ++ struct plda_msi *msi = &port->msi; + unsigned long bit; + + mutex_lock(&msi->lock); +@@ -425,8 +429,8 @@ static void mc_irq_msi_domain_free(struc + unsigned int nr_irqs) + { + struct irq_data *d = irq_domain_get_irq_data(domain, virq); +- struct mc_pcie *port = irq_data_get_irq_chip_data(d); +- struct mc_msi *msi = &port->msi; ++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(d); ++ struct plda_msi *msi = &port->msi; + + mutex_lock(&msi->lock); + +@@ -456,11 +460,11 @@ static struct msi_domain_info mc_msi_dom + .chip = &mc_msi_irq_chip, + }; + +-static int mc_allocate_msi_domains(struct mc_pcie *port) ++static int mc_allocate_msi_domains(struct plda_pcie_rp *port) + { + struct device *dev = port->dev; + struct fwnode_handle *fwnode = of_node_to_fwnode(dev->of_node); +- struct mc_msi *msi = &port->msi; ++ struct plda_msi *msi = &port->msi; + + mutex_init(&port->msi.lock); + +@@ -484,7 +488,7 @@ static int mc_allocate_msi_domains(struc + + static void mc_handle_intx(struct irq_desc *desc) + { +- struct mc_pcie *port = irq_desc_get_handler_data(desc); ++ struct plda_pcie_rp *port = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); + struct device *dev = port->dev; + void __iomem *bridge_base_addr = port->bridge_addr; +@@ -511,7 +515,7 @@ static void mc_handle_intx(struct irq_de + + static void mc_ack_intx_irq(struct irq_data *data) + { +- struct mc_pcie *port = irq_data_get_irq_chip_data(data); ++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); + void __iomem *bridge_base_addr = port->bridge_addr; + u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT); + +@@ -520,7 +524,7 @@ static void mc_ack_intx_irq(struct irq_d + + static void mc_mask_intx_irq(struct irq_data *data) + { +- struct mc_pcie *port = irq_data_get_irq_chip_data(data); ++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); + void __iomem *bridge_base_addr = port->bridge_addr; + unsigned long flags; + u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT); +@@ -535,7 +539,7 @@ static void mc_mask_intx_irq(struct irq_ + + static void mc_unmask_intx_irq(struct irq_data *data) + { +- struct mc_pcie *port = irq_data_get_irq_chip_data(data); ++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); + void __iomem *bridge_base_addr = port->bridge_addr; + unsigned long flags; + u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT); +@@ -625,21 +629,22 @@ static u32 local_events(struct mc_pcie * + return val; + } + +-static u32 get_events(struct mc_pcie *port) ++static u32 get_events(struct plda_pcie_rp *port) + { ++ struct mc_pcie *mc_port = container_of(port, struct mc_pcie, plda); + u32 events = 0; + +- events |= pcie_events(port); +- events |= sec_errors(port); +- events |= ded_errors(port); +- events |= local_events(port); ++ events |= pcie_events(mc_port); ++ events |= sec_errors(mc_port); ++ events |= ded_errors(mc_port); ++ events |= local_events(mc_port); + + return events; + } + + static irqreturn_t mc_event_handler(int irq, void *dev_id) + { +- struct mc_pcie *port = dev_id; ++ struct plda_pcie_rp *port = dev_id; + struct device *dev = port->dev; + struct irq_data *data; + +@@ -655,7 +660,7 @@ static irqreturn_t mc_event_handler(int + + static void mc_handle_event(struct irq_desc *desc) + { +- struct mc_pcie *port = irq_desc_get_handler_data(desc); ++ struct plda_pcie_rp *port = irq_desc_get_handler_data(desc); + unsigned long events; + u32 bit; + struct irq_chip *chip = irq_desc_get_chip(desc); +@@ -672,12 +677,13 @@ static void mc_handle_event(struct irq_d + + static void mc_ack_event_irq(struct irq_data *data) + { +- struct mc_pcie *port = irq_data_get_irq_chip_data(data); ++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); ++ struct mc_pcie *mc_port = container_of(port, struct mc_pcie, plda); + u32 event = data->hwirq; + void __iomem *addr; + u32 mask; + +- addr = port->axi_base_addr + event_descs[event].base + ++ addr = mc_port->axi_base_addr + event_descs[event].base + + event_descs[event].offset; + mask = event_descs[event].mask; + mask |= event_descs[event].enb_mask; +@@ -687,13 +693,14 @@ static void mc_ack_event_irq(struct irq_ + + static void mc_mask_event_irq(struct irq_data *data) + { +- struct mc_pcie *port = irq_data_get_irq_chip_data(data); ++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); ++ struct mc_pcie *mc_port = container_of(port, struct mc_pcie, plda); + u32 event = data->hwirq; + void __iomem *addr; + u32 mask; + u32 val; + +- addr = port->axi_base_addr + event_descs[event].base + ++ addr = mc_port->axi_base_addr + event_descs[event].base + + event_descs[event].mask_offset; + mask = event_descs[event].mask; + if (event_descs[event].enb_mask) { +@@ -717,13 +724,14 @@ static void mc_mask_event_irq(struct irq + + static void mc_unmask_event_irq(struct irq_data *data) + { +- struct mc_pcie *port = irq_data_get_irq_chip_data(data); ++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); ++ struct mc_pcie *mc_port = container_of(port, struct mc_pcie, plda); + u32 event = data->hwirq; + void __iomem *addr; + u32 mask; + u32 val; + +- addr = port->axi_base_addr + event_descs[event].base + ++ addr = mc_port->axi_base_addr + event_descs[event].base + + event_descs[event].mask_offset; + mask = event_descs[event].mask; + +@@ -811,7 +819,7 @@ static int mc_pcie_init_clks(struct devi + return 0; + } + +-static int mc_pcie_init_irq_domains(struct mc_pcie *port) ++static int mc_pcie_init_irq_domains(struct plda_pcie_rp *port) + { + struct device *dev = port->dev; + struct device_node *node = dev->of_node; +@@ -889,7 +897,7 @@ static void mc_pcie_setup_window(void __ + } + + static int mc_pcie_setup_windows(struct platform_device *pdev, +- struct mc_pcie *port) ++ struct plda_pcie_rp *port) + { + void __iomem *bridge_base_addr = port->bridge_addr; + struct pci_host_bridge *bridge = platform_get_drvdata(pdev); +@@ -970,7 +978,7 @@ static void mc_disable_interrupts(struct + writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_HOST); + } + +-static int mc_init_interrupts(struct platform_device *pdev, struct mc_pcie *port) ++static int mc_init_interrupts(struct platform_device *pdev, struct plda_pcie_rp *port) + { + struct device *dev = &pdev->dev; + int irq; +@@ -1043,12 +1051,12 @@ static int mc_platform_init(struct pci_c + mc_pcie_enable_msi(port, cfg->win); + + /* Configure non-config space outbound ranges */ +- ret = mc_pcie_setup_windows(pdev, port); ++ ret = mc_pcie_setup_windows(pdev, &port->plda); + if (ret) + return ret; + + /* Address translation is up; safe to enable interrupts */ +- ret = mc_init_interrupts(pdev, port); ++ ret = mc_init_interrupts(pdev, &port->plda); + if (ret) + return ret; + +@@ -1059,6 +1067,7 @@ static int mc_host_probe(struct platform + { + struct device *dev = &pdev->dev; + void __iomem *bridge_base_addr; ++ struct plda_pcie_rp *plda; + int ret; + u32 val; + +@@ -1066,7 +1075,8 @@ static int mc_host_probe(struct platform + if (!port) + return -ENOMEM; + +- port->dev = dev; ++ plda = &port->plda; ++ plda->dev = dev; + + port->axi_base_addr = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(port->axi_base_addr)) +@@ -1075,7 +1085,7 @@ static int mc_host_probe(struct platform + mc_disable_interrupts(port); + + bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; +- port->bridge_addr = bridge_base_addr; ++ plda->bridge_addr = bridge_base_addr; + + /* Allow enabling MSI by disabling MSI-X */ + val = readl(bridge_base_addr + PCIE_PCI_IRQ_DW0); +@@ -1087,10 +1097,10 @@ static int mc_host_probe(struct platform + val &= NUM_MSI_MSGS_MASK; + val >>= NUM_MSI_MSGS_SHIFT; + +- port->msi.num_vectors = 1 << val; ++ plda->msi.num_vectors = 1 << val; + + /* Pick vector address from design */ +- port->msi.vector_phy = readl_relaxed(bridge_base_addr + IMSI_ADDR); ++ plda->msi.vector_phy = readl_relaxed(bridge_base_addr + IMSI_ADDR); + + ret = mc_pcie_init_clks(dev); + if (ret) { diff --git a/target/linux/starfive/patches-6.6/0019-PCI-microchip-Move-PCIe-host-data-structures-to-plda.patch b/target/linux/starfive/patches-6.6/0019-PCI-microchip-Move-PCIe-host-data-structures-to-plda.patch new file mode 100644 index 0000000000..0823502cfd --- /dev/null +++ b/target/linux/starfive/patches-6.6/0019-PCI-microchip-Move-PCIe-host-data-structures-to-plda.patch @@ -0,0 +1,87 @@ +From a53cf9b237dd53c9538b51a8df592888935c8411 Mon Sep 17 00:00:00 2001 +From: Minda Chen +Date: Mon, 8 Jan 2024 19:05:56 +0800 +Subject: [PATCH 019/116] PCI: microchip: Move PCIe host data structures to + plda-pcie.h + +Move the common data structures definition to head file for these two data +structures can be re-used. + +Signed-off-by: Minda Chen +Reviewed-by: Conor Dooley +--- + .../pci/controller/plda/pcie-microchip-host.c | 20 ------------------ + drivers/pci/controller/plda/pcie-plda.h | 21 +++++++++++++++++++ + 2 files changed, 21 insertions(+), 20 deletions(-) + +--- a/drivers/pci/controller/plda/pcie-microchip-host.c ++++ b/drivers/pci/controller/plda/pcie-microchip-host.c +@@ -21,9 +21,6 @@ + #include "../../pci.h" + #include "pcie-plda.h" + +-/* Number of MSI IRQs */ +-#define PLDA_MAX_NUM_MSI_IRQS 32 +- + /* PCIe Bridge Phy and Controller Phy offsets */ + #define MC_PCIE1_BRIDGE_ADDR 0x00008000u + #define MC_PCIE1_CTRL_ADDR 0x0000a000u +@@ -179,23 +176,6 @@ struct event_map { + u32 event_bit; + }; + +-struct plda_msi { +- struct mutex lock; /* Protect used bitmap */ +- struct irq_domain *msi_domain; +- struct irq_domain *dev_domain; +- u32 num_vectors; +- u64 vector_phy; +- DECLARE_BITMAP(used, PLDA_MAX_NUM_MSI_IRQS); +-}; +- +-struct plda_pcie_rp { +- struct device *dev; +- struct irq_domain *intx_domain; +- struct irq_domain *event_domain; +- raw_spinlock_t lock; +- struct plda_msi msi; +- void __iomem *bridge_addr; +-}; + + struct mc_pcie { + struct plda_pcie_rp plda; +--- a/drivers/pci/controller/plda/pcie-plda.h ++++ b/drivers/pci/controller/plda/pcie-plda.h +@@ -6,6 +6,9 @@ + #ifndef _PCIE_PLDA_H + #define _PCIE_PLDA_H + ++/* Number of MSI IRQs */ ++#define PLDA_MAX_NUM_MSI_IRQS 32 ++ + /* PCIe Bridge Phy Regs */ + #define PCIE_PCI_IRQ_DW0 0xa8 + #define MSIX_CAP_MASK BIT(31) +@@ -105,4 +108,22 @@ enum plda_int_event { + + #define PLDA_MAX_INT_NUM (PLDA_NUM_DMA_EVENTS + PLDA_INT_EVENT_NUM) + ++struct plda_msi { ++ struct mutex lock; /* Protect used bitmap */ ++ struct irq_domain *msi_domain; ++ struct irq_domain *dev_domain; ++ u32 num_vectors; ++ u64 vector_phy; ++ DECLARE_BITMAP(used, PLDA_MAX_NUM_MSI_IRQS); ++}; ++ ++struct plda_pcie_rp { ++ struct device *dev; ++ struct irq_domain *intx_domain; ++ struct irq_domain *event_domain; ++ raw_spinlock_t lock; ++ struct plda_msi msi; ++ void __iomem *bridge_addr; ++}; ++ + #endif diff --git a/target/linux/starfive/patches-6.6/0020-PCI-microchip-Rename-two-setup-functions.patch b/target/linux/starfive/patches-6.6/0020-PCI-microchip-Rename-two-setup-functions.patch new file mode 100644 index 0000000000..2bed600216 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0020-PCI-microchip-Rename-two-setup-functions.patch @@ -0,0 +1,76 @@ +From d0e56d1ef7398bbf76be6e48d77943cbf644688e Mon Sep 17 00:00:00 2001 +From: Minda Chen +Date: Mon, 8 Jan 2024 19:05:57 +0800 +Subject: [PATCH 020/116] PCI: microchip: Rename two setup functions + +Rename two setup functions to plda prefix. Prepare to re-use these two +setup function. + +For two setup functions names are similar, rename mc_pcie_setup_windows() +to plda_pcie_setup_iomems(). + +Signed-off-by: Minda Chen +Reviewed-by: Conor Dooley +--- + .../pci/controller/plda/pcie-microchip-host.c | 24 +++++++++---------- + 1 file changed, 12 insertions(+), 12 deletions(-) + +--- a/drivers/pci/controller/plda/pcie-microchip-host.c ++++ b/drivers/pci/controller/plda/pcie-microchip-host.c +@@ -838,9 +838,9 @@ static int mc_pcie_init_irq_domains(stru + return mc_allocate_msi_domains(port); + } + +-static void mc_pcie_setup_window(void __iomem *bridge_base_addr, u32 index, +- phys_addr_t axi_addr, phys_addr_t pci_addr, +- size_t size) ++static void plda_pcie_setup_window(void __iomem *bridge_base_addr, u32 index, ++ phys_addr_t axi_addr, phys_addr_t pci_addr, ++ size_t size) + { + u32 atr_sz = ilog2(size) - 1; + u32 val; +@@ -876,8 +876,8 @@ static void mc_pcie_setup_window(void __ + writel(0, bridge_base_addr + ATR0_PCIE_WIN0_SRC_ADDR); + } + +-static int mc_pcie_setup_windows(struct platform_device *pdev, +- struct plda_pcie_rp *port) ++static int plda_pcie_setup_iomems(struct platform_device *pdev, ++ struct plda_pcie_rp *port) + { + void __iomem *bridge_base_addr = port->bridge_addr; + struct pci_host_bridge *bridge = platform_get_drvdata(pdev); +@@ -888,9 +888,9 @@ static int mc_pcie_setup_windows(struct + resource_list_for_each_entry(entry, &bridge->windows) { + if (resource_type(entry->res) == IORESOURCE_MEM) { + pci_addr = entry->res->start - entry->offset; +- mc_pcie_setup_window(bridge_base_addr, index, +- entry->res->start, pci_addr, +- resource_size(entry->res)); ++ plda_pcie_setup_window(bridge_base_addr, index, ++ entry->res->start, pci_addr, ++ resource_size(entry->res)); + index++; + } + } +@@ -1023,15 +1023,15 @@ static int mc_platform_init(struct pci_c + int ret; + + /* Configure address translation table 0 for PCIe config space */ +- mc_pcie_setup_window(bridge_base_addr, 0, cfg->res.start, +- cfg->res.start, +- resource_size(&cfg->res)); ++ plda_pcie_setup_window(bridge_base_addr, 0, cfg->res.start, ++ cfg->res.start, ++ resource_size(&cfg->res)); + + /* Need some fixups in config space */ + mc_pcie_enable_msi(port, cfg->win); + + /* Configure non-config space outbound ranges */ +- ret = mc_pcie_setup_windows(pdev, &port->plda); ++ ret = plda_pcie_setup_iomems(pdev, &port->plda); + if (ret) + return ret; + diff --git a/target/linux/starfive/patches-6.6/0021-PCI-microchip-Change-the-argument-of-plda_pcie_setup.patch b/target/linux/starfive/patches-6.6/0021-PCI-microchip-Change-the-argument-of-plda_pcie_setup.patch new file mode 100644 index 0000000000..de6868b9f6 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0021-PCI-microchip-Change-the-argument-of-plda_pcie_setup.patch @@ -0,0 +1,49 @@ +From 2fd7c88ef318fd39023ce1eb73f37a29fbd25d74 Mon Sep 17 00:00:00 2001 +From: Minda Chen +Date: Mon, 8 Jan 2024 19:05:58 +0800 +Subject: [PATCH 021/116] PCI: microchip: Change the argument of + plda_pcie_setup_iomems() + +If other vendor do not select PCI_HOST_COMMON, the driver data is not +struct pci_host_bridge. + +Move calling platform_get_drvdata() to mc_platform_init(). + +Signed-off-by: Minda Chen +Reviewed-by: Conor Dooley +--- + drivers/pci/controller/plda/pcie-microchip-host.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +--- a/drivers/pci/controller/plda/pcie-microchip-host.c ++++ b/drivers/pci/controller/plda/pcie-microchip-host.c +@@ -876,11 +876,10 @@ static void plda_pcie_setup_window(void + writel(0, bridge_base_addr + ATR0_PCIE_WIN0_SRC_ADDR); + } + +-static int plda_pcie_setup_iomems(struct platform_device *pdev, ++static int plda_pcie_setup_iomems(struct pci_host_bridge *bridge, + struct plda_pcie_rp *port) + { + void __iomem *bridge_base_addr = port->bridge_addr; +- struct pci_host_bridge *bridge = platform_get_drvdata(pdev); + struct resource_entry *entry; + u64 pci_addr; + u32 index = 1; +@@ -1018,6 +1017,7 @@ static int mc_platform_init(struct pci_c + { + struct device *dev = cfg->parent; + struct platform_device *pdev = to_platform_device(dev); ++ struct pci_host_bridge *bridge = platform_get_drvdata(pdev); + void __iomem *bridge_base_addr = + port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; + int ret; +@@ -1031,7 +1031,7 @@ static int mc_platform_init(struct pci_c + mc_pcie_enable_msi(port, cfg->win); + + /* Configure non-config space outbound ranges */ +- ret = plda_pcie_setup_iomems(pdev, &port->plda); ++ ret = plda_pcie_setup_iomems(bridge, &port->plda); + if (ret) + return ret; + diff --git a/target/linux/starfive/patches-6.6/0022-PCI-microchip-Move-setup-functions-to-pcie-plda-host.patch b/target/linux/starfive/patches-6.6/0022-PCI-microchip-Move-setup-functions-to-pcie-plda-host.patch new file mode 100644 index 0000000000..62f458be4a --- /dev/null +++ b/target/linux/starfive/patches-6.6/0022-PCI-microchip-Move-setup-functions-to-pcie-plda-host.patch @@ -0,0 +1,200 @@ +From 201ce99897ff5fff2612cb633406e90c1b2acbcf Mon Sep 17 00:00:00 2001 +From: Minda Chen +Date: Mon, 8 Jan 2024 19:05:59 +0800 +Subject: [PATCH 022/116] PCI: microchip: Move setup functions to + pcie-plda-host.c + +Move setup functions to common pcie-plda-host.c. So these two functions +can be re-used. + +Signed-off-by: Minda Chen +Reviewed-by: Conor Dooley +--- + drivers/pci/controller/plda/Kconfig | 4 + + drivers/pci/controller/plda/Makefile | 1 + + .../pci/controller/plda/pcie-microchip-host.c | 59 --------------- + drivers/pci/controller/plda/pcie-plda-host.c | 74 +++++++++++++++++++ + drivers/pci/controller/plda/pcie-plda.h | 5 ++ + 5 files changed, 84 insertions(+), 59 deletions(-) + create mode 100644 drivers/pci/controller/plda/pcie-plda-host.c + +--- a/drivers/pci/controller/plda/Kconfig ++++ b/drivers/pci/controller/plda/Kconfig +@@ -3,10 +3,14 @@ + menu "PLDA-based PCIe controllers" + depends on PCI + ++config PCIE_PLDA_HOST ++ bool ++ + config PCIE_MICROCHIP_HOST + tristate "Microchip AXI PCIe controller" + depends on PCI_MSI && OF + select PCI_HOST_COMMON ++ select PCIE_PLDA_HOST + help + Say Y here if you want kernel to support the Microchip AXI PCIe + Host Bridge driver. +--- a/drivers/pci/controller/plda/Makefile ++++ b/drivers/pci/controller/plda/Makefile +@@ -1,2 +1,3 @@ + # SPDX-License-Identifier: GPL-2.0 ++obj-$(CONFIG_PCIE_PLDA_HOST) += pcie-plda-host.o + obj-$(CONFIG_PCIE_MICROCHIP_HOST) += pcie-microchip-host.o +--- a/drivers/pci/controller/plda/pcie-microchip-host.c ++++ b/drivers/pci/controller/plda/pcie-microchip-host.c +@@ -838,65 +838,6 @@ static int mc_pcie_init_irq_domains(stru + return mc_allocate_msi_domains(port); + } + +-static void plda_pcie_setup_window(void __iomem *bridge_base_addr, u32 index, +- phys_addr_t axi_addr, phys_addr_t pci_addr, +- size_t size) +-{ +- u32 atr_sz = ilog2(size) - 1; +- u32 val; +- +- if (index == 0) +- val = PCIE_CONFIG_INTERFACE; +- else +- val = PCIE_TX_RX_INTERFACE; +- +- writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + +- ATR0_AXI4_SLV0_TRSL_PARAM); +- +- val = lower_32_bits(axi_addr) | (atr_sz << ATR_SIZE_SHIFT) | +- ATR_IMPL_ENABLE; +- writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + +- ATR0_AXI4_SLV0_SRCADDR_PARAM); +- +- val = upper_32_bits(axi_addr); +- writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + +- ATR0_AXI4_SLV0_SRC_ADDR); +- +- val = lower_32_bits(pci_addr); +- writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + +- ATR0_AXI4_SLV0_TRSL_ADDR_LSB); +- +- val = upper_32_bits(pci_addr); +- writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + +- ATR0_AXI4_SLV0_TRSL_ADDR_UDW); +- +- val = readl(bridge_base_addr + ATR0_PCIE_WIN0_SRCADDR_PARAM); +- val |= (ATR0_PCIE_ATR_SIZE << ATR0_PCIE_ATR_SIZE_SHIFT); +- writel(val, bridge_base_addr + ATR0_PCIE_WIN0_SRCADDR_PARAM); +- writel(0, bridge_base_addr + ATR0_PCIE_WIN0_SRC_ADDR); +-} +- +-static int plda_pcie_setup_iomems(struct pci_host_bridge *bridge, +- struct plda_pcie_rp *port) +-{ +- void __iomem *bridge_base_addr = port->bridge_addr; +- struct resource_entry *entry; +- u64 pci_addr; +- u32 index = 1; +- +- resource_list_for_each_entry(entry, &bridge->windows) { +- if (resource_type(entry->res) == IORESOURCE_MEM) { +- pci_addr = entry->res->start - entry->offset; +- plda_pcie_setup_window(bridge_base_addr, index, +- entry->res->start, pci_addr, +- resource_size(entry->res)); +- index++; +- } +- } +- +- return 0; +-} +- + static inline void mc_clear_secs(struct mc_pcie *port) + { + void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR; +--- /dev/null ++++ b/drivers/pci/controller/plda/pcie-plda-host.c +@@ -0,0 +1,74 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * PLDA PCIe XpressRich host controller driver ++ * ++ * Copyright (C) 2023 Microchip Co. Ltd ++ * ++ * Author: Daire McNamara ++ */ ++ ++#include ++#include ++ ++#include "pcie-plda.h" ++ ++void plda_pcie_setup_window(void __iomem *bridge_base_addr, u32 index, ++ phys_addr_t axi_addr, phys_addr_t pci_addr, ++ size_t size) ++{ ++ u32 atr_sz = ilog2(size) - 1; ++ u32 val; ++ ++ if (index == 0) ++ val = PCIE_CONFIG_INTERFACE; ++ else ++ val = PCIE_TX_RX_INTERFACE; ++ ++ writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + ++ ATR0_AXI4_SLV0_TRSL_PARAM); ++ ++ val = lower_32_bits(axi_addr) | (atr_sz << ATR_SIZE_SHIFT) | ++ ATR_IMPL_ENABLE; ++ writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + ++ ATR0_AXI4_SLV0_SRCADDR_PARAM); ++ ++ val = upper_32_bits(axi_addr); ++ writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + ++ ATR0_AXI4_SLV0_SRC_ADDR); ++ ++ val = lower_32_bits(pci_addr); ++ writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + ++ ATR0_AXI4_SLV0_TRSL_ADDR_LSB); ++ ++ val = upper_32_bits(pci_addr); ++ writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + ++ ATR0_AXI4_SLV0_TRSL_ADDR_UDW); ++ ++ val = readl(bridge_base_addr + ATR0_PCIE_WIN0_SRCADDR_PARAM); ++ val |= (ATR0_PCIE_ATR_SIZE << ATR0_PCIE_ATR_SIZE_SHIFT); ++ writel(val, bridge_base_addr + ATR0_PCIE_WIN0_SRCADDR_PARAM); ++ writel(0, bridge_base_addr + ATR0_PCIE_WIN0_SRC_ADDR); ++} ++EXPORT_SYMBOL_GPL(plda_pcie_setup_window); ++ ++int plda_pcie_setup_iomems(struct pci_host_bridge *bridge, ++ struct plda_pcie_rp *port) ++{ ++ void __iomem *bridge_base_addr = port->bridge_addr; ++ struct resource_entry *entry; ++ u64 pci_addr; ++ u32 index = 1; ++ ++ resource_list_for_each_entry(entry, &bridge->windows) { ++ if (resource_type(entry->res) == IORESOURCE_MEM) { ++ pci_addr = entry->res->start - entry->offset; ++ plda_pcie_setup_window(bridge_base_addr, index, ++ entry->res->start, pci_addr, ++ resource_size(entry->res)); ++ index++; ++ } ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(plda_pcie_setup_iomems); +--- a/drivers/pci/controller/plda/pcie-plda.h ++++ b/drivers/pci/controller/plda/pcie-plda.h +@@ -126,4 +126,9 @@ struct plda_pcie_rp { + void __iomem *bridge_addr; + }; + ++void plda_pcie_setup_window(void __iomem *bridge_base_addr, u32 index, ++ phys_addr_t axi_addr, phys_addr_t pci_addr, ++ size_t size); ++int plda_pcie_setup_iomems(struct pci_host_bridge *bridge, ++ struct plda_pcie_rp *port); + #endif diff --git a/target/linux/starfive/patches-6.6/0023-PCI-microchip-Rename-interrupt-related-functions.patch b/target/linux/starfive/patches-6.6/0023-PCI-microchip-Rename-interrupt-related-functions.patch new file mode 100644 index 0000000000..09c1633b6f --- /dev/null +++ b/target/linux/starfive/patches-6.6/0023-PCI-microchip-Rename-interrupt-related-functions.patch @@ -0,0 +1,336 @@ +From 2a48bc45dcf8cbe736b594d013cfa9d682214c43 Mon Sep 17 00:00:00 2001 +From: Minda Chen +Date: Mon, 8 Jan 2024 19:06:00 +0800 +Subject: [PATCH 023/116] PCI: microchip: Rename interrupt related functions + +Rename mc_* to plda_* for IRQ functions and related IRQ domain ops data +instances. + +MSI, INTx interrupt code and IRQ init code are all can be re-used. + +Signed-off-by: Minda Chen +Acked-by: Conor Dooley +--- + .../pci/controller/plda/pcie-microchip-host.c | 109 +++++++++--------- + 1 file changed, 57 insertions(+), 52 deletions(-) + +--- a/drivers/pci/controller/plda/pcie-microchip-host.c ++++ b/drivers/pci/controller/plda/pcie-microchip-host.c +@@ -318,7 +318,7 @@ static void mc_pcie_enable_msi(struct mc + ecam + MC_MSI_CAP_CTRL_OFFSET + PCI_MSI_ADDRESS_HI); + } + +-static void mc_handle_msi(struct irq_desc *desc) ++static void plda_handle_msi(struct irq_desc *desc) + { + struct plda_pcie_rp *port = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); +@@ -346,7 +346,7 @@ static void mc_handle_msi(struct irq_des + chained_irq_exit(chip, desc); + } + +-static void mc_msi_bottom_irq_ack(struct irq_data *data) ++static void plda_msi_bottom_irq_ack(struct irq_data *data) + { + struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); + void __iomem *bridge_base_addr = port->bridge_addr; +@@ -355,7 +355,7 @@ static void mc_msi_bottom_irq_ack(struct + writel_relaxed(BIT(bitpos), bridge_base_addr + ISTATUS_MSI); + } + +-static void mc_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) ++static void plda_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) + { + struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); + phys_addr_t addr = port->msi.vector_phy; +@@ -368,21 +368,23 @@ static void mc_compose_msi_msg(struct ir + (int)data->hwirq, msg->address_hi, msg->address_lo); + } + +-static int mc_msi_set_affinity(struct irq_data *irq_data, +- const struct cpumask *mask, bool force) ++static int plda_msi_set_affinity(struct irq_data *irq_data, ++ const struct cpumask *mask, bool force) + { + return -EINVAL; + } + +-static struct irq_chip mc_msi_bottom_irq_chip = { +- .name = "Microchip MSI", +- .irq_ack = mc_msi_bottom_irq_ack, +- .irq_compose_msi_msg = mc_compose_msi_msg, +- .irq_set_affinity = mc_msi_set_affinity, ++static struct irq_chip plda_msi_bottom_irq_chip = { ++ .name = "PLDA MSI", ++ .irq_ack = plda_msi_bottom_irq_ack, ++ .irq_compose_msi_msg = plda_compose_msi_msg, ++ .irq_set_affinity = plda_msi_set_affinity, + }; + +-static int mc_irq_msi_domain_alloc(struct irq_domain *domain, unsigned int virq, +- unsigned int nr_irqs, void *args) ++static int plda_irq_msi_domain_alloc(struct irq_domain *domain, ++ unsigned int virq, ++ unsigned int nr_irqs, ++ void *args) + { + struct plda_pcie_rp *port = domain->host_data; + struct plda_msi *msi = &port->msi; +@@ -397,7 +399,7 @@ static int mc_irq_msi_domain_alloc(struc + + set_bit(bit, msi->used); + +- irq_domain_set_info(domain, virq, bit, &mc_msi_bottom_irq_chip, ++ irq_domain_set_info(domain, virq, bit, &plda_msi_bottom_irq_chip, + domain->host_data, handle_edge_irq, NULL, NULL); + + mutex_unlock(&msi->lock); +@@ -405,8 +407,9 @@ static int mc_irq_msi_domain_alloc(struc + return 0; + } + +-static void mc_irq_msi_domain_free(struct irq_domain *domain, unsigned int virq, +- unsigned int nr_irqs) ++static void plda_irq_msi_domain_free(struct irq_domain *domain, ++ unsigned int virq, ++ unsigned int nr_irqs) + { + struct irq_data *d = irq_domain_get_irq_data(domain, virq); + struct plda_pcie_rp *port = irq_data_get_irq_chip_data(d); +@@ -423,24 +426,24 @@ static void mc_irq_msi_domain_free(struc + } + + static const struct irq_domain_ops msi_domain_ops = { +- .alloc = mc_irq_msi_domain_alloc, +- .free = mc_irq_msi_domain_free, ++ .alloc = plda_irq_msi_domain_alloc, ++ .free = plda_irq_msi_domain_free, + }; + +-static struct irq_chip mc_msi_irq_chip = { +- .name = "Microchip PCIe MSI", ++static struct irq_chip plda_msi_irq_chip = { ++ .name = "PLDA PCIe MSI", + .irq_ack = irq_chip_ack_parent, + .irq_mask = pci_msi_mask_irq, + .irq_unmask = pci_msi_unmask_irq, + }; + +-static struct msi_domain_info mc_msi_domain_info = { ++static struct msi_domain_info plda_msi_domain_info = { + .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | + MSI_FLAG_PCI_MSIX), +- .chip = &mc_msi_irq_chip, ++ .chip = &plda_msi_irq_chip, + }; + +-static int mc_allocate_msi_domains(struct plda_pcie_rp *port) ++static int plda_allocate_msi_domains(struct plda_pcie_rp *port) + { + struct device *dev = port->dev; + struct fwnode_handle *fwnode = of_node_to_fwnode(dev->of_node); +@@ -455,7 +458,8 @@ static int mc_allocate_msi_domains(struc + return -ENOMEM; + } + +- msi->msi_domain = pci_msi_create_irq_domain(fwnode, &mc_msi_domain_info, ++ msi->msi_domain = pci_msi_create_irq_domain(fwnode, ++ &plda_msi_domain_info, + msi->dev_domain); + if (!msi->msi_domain) { + dev_err(dev, "failed to create MSI domain\n"); +@@ -466,7 +470,7 @@ static int mc_allocate_msi_domains(struc + return 0; + } + +-static void mc_handle_intx(struct irq_desc *desc) ++static void plda_handle_intx(struct irq_desc *desc) + { + struct plda_pcie_rp *port = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); +@@ -493,7 +497,7 @@ static void mc_handle_intx(struct irq_de + chained_irq_exit(chip, desc); + } + +-static void mc_ack_intx_irq(struct irq_data *data) ++static void plda_ack_intx_irq(struct irq_data *data) + { + struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); + void __iomem *bridge_base_addr = port->bridge_addr; +@@ -502,7 +506,7 @@ static void mc_ack_intx_irq(struct irq_d + writel_relaxed(mask, bridge_base_addr + ISTATUS_LOCAL); + } + +-static void mc_mask_intx_irq(struct irq_data *data) ++static void plda_mask_intx_irq(struct irq_data *data) + { + struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); + void __iomem *bridge_base_addr = port->bridge_addr; +@@ -517,7 +521,7 @@ static void mc_mask_intx_irq(struct irq_ + raw_spin_unlock_irqrestore(&port->lock, flags); + } + +-static void mc_unmask_intx_irq(struct irq_data *data) ++static void plda_unmask_intx_irq(struct irq_data *data) + { + struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); + void __iomem *bridge_base_addr = port->bridge_addr; +@@ -532,24 +536,24 @@ static void mc_unmask_intx_irq(struct ir + raw_spin_unlock_irqrestore(&port->lock, flags); + } + +-static struct irq_chip mc_intx_irq_chip = { +- .name = "Microchip PCIe INTx", +- .irq_ack = mc_ack_intx_irq, +- .irq_mask = mc_mask_intx_irq, +- .irq_unmask = mc_unmask_intx_irq, ++static struct irq_chip plda_intx_irq_chip = { ++ .name = "PLDA PCIe INTx", ++ .irq_ack = plda_ack_intx_irq, ++ .irq_mask = plda_mask_intx_irq, ++ .irq_unmask = plda_unmask_intx_irq, + }; + +-static int mc_pcie_intx_map(struct irq_domain *domain, unsigned int irq, +- irq_hw_number_t hwirq) ++static int plda_pcie_intx_map(struct irq_domain *domain, unsigned int irq, ++ irq_hw_number_t hwirq) + { +- irq_set_chip_and_handler(irq, &mc_intx_irq_chip, handle_level_irq); ++ irq_set_chip_and_handler(irq, &plda_intx_irq_chip, handle_level_irq); + irq_set_chip_data(irq, domain->host_data); + + return 0; + } + + static const struct irq_domain_ops intx_domain_ops = { +- .map = mc_pcie_intx_map, ++ .map = plda_pcie_intx_map, + }; + + static inline u32 reg_to_event(u32 reg, struct event_map field) +@@ -609,7 +613,7 @@ static u32 local_events(struct mc_pcie * + return val; + } + +-static u32 get_events(struct plda_pcie_rp *port) ++static u32 mc_get_events(struct plda_pcie_rp *port) + { + struct mc_pcie *mc_port = container_of(port, struct mc_pcie, plda); + u32 events = 0; +@@ -638,7 +642,7 @@ static irqreturn_t mc_event_handler(int + return IRQ_HANDLED; + } + +-static void mc_handle_event(struct irq_desc *desc) ++static void plda_handle_event(struct irq_desc *desc) + { + struct plda_pcie_rp *port = irq_desc_get_handler_data(desc); + unsigned long events; +@@ -647,7 +651,7 @@ static void mc_handle_event(struct irq_d + + chained_irq_enter(chip, desc); + +- events = get_events(port); ++ events = mc_get_events(port); + + for_each_set_bit(bit, &events, NUM_EVENTS) + generic_handle_domain_irq(port->event_domain, bit); +@@ -741,8 +745,8 @@ static struct irq_chip mc_event_irq_chip + .irq_unmask = mc_unmask_event_irq, + }; + +-static int mc_pcie_event_map(struct irq_domain *domain, unsigned int irq, +- irq_hw_number_t hwirq) ++static int plda_pcie_event_map(struct irq_domain *domain, unsigned int irq, ++ irq_hw_number_t hwirq) + { + irq_set_chip_and_handler(irq, &mc_event_irq_chip, handle_level_irq); + irq_set_chip_data(irq, domain->host_data); +@@ -750,8 +754,8 @@ static int mc_pcie_event_map(struct irq_ + return 0; + } + +-static const struct irq_domain_ops event_domain_ops = { +- .map = mc_pcie_event_map, ++static const struct irq_domain_ops plda_event_domain_ops = { ++ .map = plda_pcie_event_map, + }; + + static inline void mc_pcie_deinit_clk(void *data) +@@ -799,7 +803,7 @@ static int mc_pcie_init_clks(struct devi + return 0; + } + +-static int mc_pcie_init_irq_domains(struct plda_pcie_rp *port) ++static int plda_pcie_init_irq_domains(struct plda_pcie_rp *port) + { + struct device *dev = port->dev; + struct device_node *node = dev->of_node; +@@ -813,7 +817,8 @@ static int mc_pcie_init_irq_domains(stru + } + + port->event_domain = irq_domain_add_linear(pcie_intc_node, NUM_EVENTS, +- &event_domain_ops, port); ++ &plda_event_domain_ops, ++ port); + if (!port->event_domain) { + dev_err(dev, "failed to get event domain\n"); + of_node_put(pcie_intc_node); +@@ -835,7 +840,7 @@ static int mc_pcie_init_irq_domains(stru + of_node_put(pcie_intc_node); + raw_spin_lock_init(&port->lock); + +- return mc_allocate_msi_domains(port); ++ return plda_allocate_msi_domains(port); + } + + static inline void mc_clear_secs(struct mc_pcie *port) +@@ -898,14 +903,14 @@ static void mc_disable_interrupts(struct + writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_HOST); + } + +-static int mc_init_interrupts(struct platform_device *pdev, struct plda_pcie_rp *port) ++static int plda_init_interrupts(struct platform_device *pdev, struct plda_pcie_rp *port) + { + struct device *dev = &pdev->dev; + int irq; + int i, intx_irq, msi_irq, event_irq; + int ret; + +- ret = mc_pcie_init_irq_domains(port); ++ ret = plda_pcie_init_irq_domains(port); + if (ret) { + dev_err(dev, "failed creating IRQ domains\n"); + return ret; +@@ -938,7 +943,7 @@ static int mc_init_interrupts(struct pla + } + + /* Plug the INTx chained handler */ +- irq_set_chained_handler_and_data(intx_irq, mc_handle_intx, port); ++ irq_set_chained_handler_and_data(intx_irq, plda_handle_intx, port); + + msi_irq = irq_create_mapping(port->event_domain, + EVENT_LOCAL_PM_MSI_INT_MSI); +@@ -946,10 +951,10 @@ static int mc_init_interrupts(struct pla + return -ENXIO; + + /* Plug the MSI chained handler */ +- irq_set_chained_handler_and_data(msi_irq, mc_handle_msi, port); ++ irq_set_chained_handler_and_data(msi_irq, plda_handle_msi, port); + + /* Plug the main event chained handler */ +- irq_set_chained_handler_and_data(irq, mc_handle_event, port); ++ irq_set_chained_handler_and_data(irq, plda_handle_event, port); + + return 0; + } +@@ -977,7 +982,7 @@ static int mc_platform_init(struct pci_c + return ret; + + /* Address translation is up; safe to enable interrupts */ +- ret = mc_init_interrupts(pdev, &port->plda); ++ ret = plda_init_interrupts(pdev, &port->plda); + if (ret) + return ret; + diff --git a/target/linux/starfive/patches-6.6/0024-PCI-microchip-Add-num_events-field-to-struct-plda_pc.patch b/target/linux/starfive/patches-6.6/0024-PCI-microchip-Add-num_events-field-to-struct-plda_pc.patch new file mode 100644 index 0000000000..4071feb1b5 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0024-PCI-microchip-Add-num_events-field-to-struct-plda_pc.patch @@ -0,0 +1,65 @@ +From ab04dadb45a4150c9fd55b97fdd7397f4739a629 Mon Sep 17 00:00:00 2001 +From: Minda Chen +Date: Mon, 8 Jan 2024 19:06:01 +0800 +Subject: [PATCH 024/116] PCI: microchip: Add num_events field to struct + plda_pcie_rp + +The number of events is different across platforms. In order to share +interrupt processing code, add a variable that defines the number of +events so that it can be set per-platform instead of hardcoding it. + +Signed-off-by: Minda Chen +Reviewed-by: Conor Dooley +--- + drivers/pci/controller/plda/pcie-microchip-host.c | 8 +++++--- + drivers/pci/controller/plda/pcie-plda.h | 1 + + 2 files changed, 6 insertions(+), 3 deletions(-) + +--- a/drivers/pci/controller/plda/pcie-microchip-host.c ++++ b/drivers/pci/controller/plda/pcie-microchip-host.c +@@ -653,7 +653,7 @@ static void plda_handle_event(struct irq + + events = mc_get_events(port); + +- for_each_set_bit(bit, &events, NUM_EVENTS) ++ for_each_set_bit(bit, &events, port->num_events) + generic_handle_domain_irq(port->event_domain, bit); + + chained_irq_exit(chip, desc); +@@ -816,7 +816,8 @@ static int plda_pcie_init_irq_domains(st + return -EINVAL; + } + +- port->event_domain = irq_domain_add_linear(pcie_intc_node, NUM_EVENTS, ++ port->event_domain = irq_domain_add_linear(pcie_intc_node, ++ port->num_events, + &plda_event_domain_ops, + port); + if (!port->event_domain) { +@@ -920,7 +921,7 @@ static int plda_init_interrupts(struct p + if (irq < 0) + return -ENODEV; + +- for (i = 0; i < NUM_EVENTS; i++) { ++ for (i = 0; i < port->num_events; i++) { + event_irq = irq_create_mapping(port->event_domain, i); + if (!event_irq) { + dev_err(dev, "failed to map hwirq %d\n", i); +@@ -1012,6 +1013,7 @@ static int mc_host_probe(struct platform + + bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; + plda->bridge_addr = bridge_base_addr; ++ plda->num_events = NUM_EVENTS; + + /* Allow enabling MSI by disabling MSI-X */ + val = readl(bridge_base_addr + PCIE_PCI_IRQ_DW0); +--- a/drivers/pci/controller/plda/pcie-plda.h ++++ b/drivers/pci/controller/plda/pcie-plda.h +@@ -124,6 +124,7 @@ struct plda_pcie_rp { + raw_spinlock_t lock; + struct plda_msi msi; + void __iomem *bridge_addr; ++ int num_events; + }; + + void plda_pcie_setup_window(void __iomem *bridge_base_addr, u32 index, diff --git a/target/linux/starfive/patches-6.6/0025-PCI-microchip-Add-request_event_irq-callback-functio.patch b/target/linux/starfive/patches-6.6/0025-PCI-microchip-Add-request_event_irq-callback-functio.patch new file mode 100644 index 0000000000..2b299e01e9 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0025-PCI-microchip-Add-request_event_irq-callback-functio.patch @@ -0,0 +1,114 @@ +From 9f202f211cc79eefecbb03715c884e54eb95a62c Mon Sep 17 00:00:00 2001 +From: Minda Chen +Date: Mon, 8 Jan 2024 19:06:02 +0800 +Subject: [PATCH 025/116] PCI: microchip: Add request_event_irq() callback + function + +As PLDA dts binding doc(Documentation/devicetree/bindings/pci/ +plda,xpressrich3-axi-common.yaml) showes, PLDA PCIe contains an interrupt +controller. Microchip Polarfire PCIe add some PCIe interrupts base on +PLDA IP interrupt controller. + +Microchip Polarfire PCIe additional intrerrupts: +EVENT_PCIE_L2_EXIT +EVENT_PCIE_HOTRST_EXIT +EVENT_PCIE_DLUP_EXIT +EVENT_SEC_TX_RAM_SEC_ERR +EVENT_SEC_RX_RAM_SEC_ERR +.... + +Both codes of register interrupts and mc_event_handler() contain +additional interrupts symbol names, these can not be re-used. So add a +new plda_event_handler() functions, which implements PLDA interrupt +defalt handler. Add request_event_irq() callback function to +compat Microchip Polorfire PCIe additional interrupts. + +Signed-off-by: Minda Chen +Acked-by: Conor Dooley +--- + .../pci/controller/plda/pcie-microchip-host.c | 31 ++++++++++++++++--- + drivers/pci/controller/plda/pcie-plda.h | 5 +++ + 2 files changed, 32 insertions(+), 4 deletions(-) + +--- a/drivers/pci/controller/plda/pcie-microchip-host.c ++++ b/drivers/pci/controller/plda/pcie-microchip-host.c +@@ -642,6 +642,11 @@ static irqreturn_t mc_event_handler(int + return IRQ_HANDLED; + } + ++static irqreturn_t plda_event_handler(int irq, void *dev_id) ++{ ++ return IRQ_HANDLED; ++} ++ + static void plda_handle_event(struct irq_desc *desc) + { + struct plda_pcie_rp *port = irq_desc_get_handler_data(desc); +@@ -803,6 +808,17 @@ static int mc_pcie_init_clks(struct devi + return 0; + } + ++static int mc_request_event_irq(struct plda_pcie_rp *plda, int event_irq, ++ int event) ++{ ++ return devm_request_irq(plda->dev, event_irq, mc_event_handler, ++ 0, event_cause[event].sym, plda); ++} ++ ++static const struct plda_event mc_event = { ++ .request_event_irq = mc_request_event_irq, ++}; ++ + static int plda_pcie_init_irq_domains(struct plda_pcie_rp *port) + { + struct device *dev = port->dev; +@@ -904,7 +920,9 @@ static void mc_disable_interrupts(struct + writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_HOST); + } + +-static int plda_init_interrupts(struct platform_device *pdev, struct plda_pcie_rp *port) ++static int plda_init_interrupts(struct platform_device *pdev, ++ struct plda_pcie_rp *port, ++ const struct plda_event *event) + { + struct device *dev = &pdev->dev; + int irq; +@@ -928,8 +946,13 @@ static int plda_init_interrupts(struct p + return -ENXIO; + } + +- ret = devm_request_irq(dev, event_irq, mc_event_handler, +- 0, event_cause[i].sym, port); ++ if (event->request_event_irq) ++ ret = event->request_event_irq(port, event_irq, i); ++ else ++ ret = devm_request_irq(dev, event_irq, ++ plda_event_handler, ++ 0, NULL, port); ++ + if (ret) { + dev_err(dev, "failed to request IRQ %d\n", event_irq); + return ret; +@@ -983,7 +1006,7 @@ static int mc_platform_init(struct pci_c + return ret; + + /* Address translation is up; safe to enable interrupts */ +- ret = plda_init_interrupts(pdev, &port->plda); ++ ret = plda_init_interrupts(pdev, &port->plda, &mc_event); + if (ret) + return ret; + +--- a/drivers/pci/controller/plda/pcie-plda.h ++++ b/drivers/pci/controller/plda/pcie-plda.h +@@ -127,6 +127,11 @@ struct plda_pcie_rp { + int num_events; + }; + ++struct plda_event { ++ int (*request_event_irq)(struct plda_pcie_rp *pcie, ++ int event_irq, int event); ++}; ++ + void plda_pcie_setup_window(void __iomem *bridge_base_addr, u32 index, + phys_addr_t axi_addr, phys_addr_t pci_addr, + size_t size); diff --git a/target/linux/starfive/patches-6.6/0026-PCI-microchip-Add-INTx-and-MSI-event-num-to-struct-p.patch b/target/linux/starfive/patches-6.6/0026-PCI-microchip-Add-INTx-and-MSI-event-num-to-struct-p.patch new file mode 100644 index 0000000000..85b8143268 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0026-PCI-microchip-Add-INTx-and-MSI-event-num-to-struct-p.patch @@ -0,0 +1,56 @@ +From 3cdc20d9cc029ba9444be111bf4e55fd5331ccbe Mon Sep 17 00:00:00 2001 +From: Minda Chen +Date: Mon, 8 Jan 2024 19:06:03 +0800 +Subject: [PATCH 026/116] PCI: microchip: Add INTx and MSI event num to struct + plda_event + +The INTx and MSI interrupt event num is different in Microchip and +StarFive platform. + +Signed-off-by: Minda Chen +Acked-by: Conor Dooley +--- + drivers/pci/controller/plda/pcie-microchip-host.c | 6 ++++-- + drivers/pci/controller/plda/pcie-plda.h | 2 ++ + 2 files changed, 6 insertions(+), 2 deletions(-) + +--- a/drivers/pci/controller/plda/pcie-microchip-host.c ++++ b/drivers/pci/controller/plda/pcie-microchip-host.c +@@ -817,6 +817,8 @@ static int mc_request_event_irq(struct p + + static const struct plda_event mc_event = { + .request_event_irq = mc_request_event_irq, ++ .intx_event = EVENT_LOCAL_PM_MSI_INT_INTX, ++ .msi_event = EVENT_LOCAL_PM_MSI_INT_MSI, + }; + + static int plda_pcie_init_irq_domains(struct plda_pcie_rp *port) +@@ -960,7 +962,7 @@ static int plda_init_interrupts(struct p + } + + intx_irq = irq_create_mapping(port->event_domain, +- EVENT_LOCAL_PM_MSI_INT_INTX); ++ event->intx_event); + if (!intx_irq) { + dev_err(dev, "failed to map INTx interrupt\n"); + return -ENXIO; +@@ -970,7 +972,7 @@ static int plda_init_interrupts(struct p + irq_set_chained_handler_and_data(intx_irq, plda_handle_intx, port); + + msi_irq = irq_create_mapping(port->event_domain, +- EVENT_LOCAL_PM_MSI_INT_MSI); ++ event->msi_event); + if (!msi_irq) + return -ENXIO; + +--- a/drivers/pci/controller/plda/pcie-plda.h ++++ b/drivers/pci/controller/plda/pcie-plda.h +@@ -130,6 +130,8 @@ struct plda_pcie_rp { + struct plda_event { + int (*request_event_irq)(struct plda_pcie_rp *pcie, + int event_irq, int event); ++ int intx_event; ++ int msi_event; + }; + + void plda_pcie_setup_window(void __iomem *bridge_base_addr, u32 index, diff --git a/target/linux/starfive/patches-6.6/0027-PCI-microchip-Add-get_events-callback-and-add-PLDA-g.patch b/target/linux/starfive/patches-6.6/0027-PCI-microchip-Add-get_events-callback-and-add-PLDA-g.patch new file mode 100644 index 0000000000..a0cb4789b8 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0027-PCI-microchip-Add-get_events-callback-and-add-PLDA-g.patch @@ -0,0 +1,167 @@ +From b4a38ef87661f21fe2fb3e085ae98f25f78aaf99 Mon Sep 17 00:00:00 2001 +From: Minda Chen +Date: Mon, 8 Jan 2024 19:06:04 +0800 +Subject: [PATCH 027/116] PCI: microchip: Add get_events() callback and add + PLDA get_event() + +As PLDA dts binding doc(Documentation/devicetree/bindings/pci/ +plda,xpressrich3-axi-common.yaml) showes, PLDA PCIe contains an interrupt +controller. + +PolarFire implements its own PCIe interrupts, additional to the regular +PCIe interrupts, due to lack of an MSI controller, so the interrupt to +event number mapping is different to the PLDA regular interrupts, +necessitating a custom get_events() implementation. + +Microchip Polarfire PCIe additional intrerrupts: +EVENT_PCIE_L2_EXIT +EVENT_PCIE_HOTRST_EXIT +EVENT_PCIE_DLUP_EXIT +EVENT_SEC_TX_RAM_SEC_ERR +EVENT_SEC_RX_RAM_SEC_ERR +.... + +plda_get_events() adds interrupt register to PLDA local event num mapping +codes. All The PLDA interrupts can be seen in new added graph. + +Signed-off-by: Minda Chen +Acked-by: Conor Dooley +--- + .../pci/controller/plda/pcie-microchip-host.c | 35 ++++++++++++++++++- + drivers/pci/controller/plda/pcie-plda.h | 32 +++++++++++++++++ + 2 files changed, 66 insertions(+), 1 deletion(-) + +--- a/drivers/pci/controller/plda/pcie-microchip-host.c ++++ b/drivers/pci/controller/plda/pcie-microchip-host.c +@@ -626,6 +626,26 @@ static u32 mc_get_events(struct plda_pci + return events; + } + ++static u32 plda_get_events(struct plda_pcie_rp *port) ++{ ++ u32 events, val, origin; ++ ++ origin = readl_relaxed(port->bridge_addr + ISTATUS_LOCAL); ++ ++ /* MSI event and sys events */ ++ val = (origin & SYS_AND_MSI_MASK) >> PM_MSI_INT_MSI_SHIFT; ++ events = val << (PM_MSI_INT_MSI_SHIFT - PCI_NUM_INTX + 1); ++ ++ /* INTx events */ ++ if (origin & PM_MSI_INT_INTX_MASK) ++ events |= BIT(PM_MSI_INT_INTX_SHIFT); ++ ++ /* remains are same with register */ ++ events |= origin & GENMASK(P_ATR_EVT_DOORBELL_SHIFT, 0); ++ ++ return events; ++} ++ + static irqreturn_t mc_event_handler(int irq, void *dev_id) + { + struct plda_pcie_rp *port = dev_id; +@@ -656,7 +676,7 @@ static void plda_handle_event(struct irq + + chained_irq_enter(chip, desc); + +- events = mc_get_events(port); ++ events = port->event_ops->get_events(port); + + for_each_set_bit(bit, &events, port->num_events) + generic_handle_domain_irq(port->event_domain, bit); +@@ -750,6 +770,10 @@ static struct irq_chip mc_event_irq_chip + .irq_unmask = mc_unmask_event_irq, + }; + ++static const struct plda_event_ops plda_event_ops = { ++ .get_events = plda_get_events, ++}; ++ + static int plda_pcie_event_map(struct irq_domain *domain, unsigned int irq, + irq_hw_number_t hwirq) + { +@@ -815,6 +839,10 @@ static int mc_request_event_irq(struct p + 0, event_cause[event].sym, plda); + } + ++static const struct plda_event_ops mc_event_ops = { ++ .get_events = mc_get_events, ++}; ++ + static const struct plda_event mc_event = { + .request_event_irq = mc_request_event_irq, + .intx_event = EVENT_LOCAL_PM_MSI_INT_INTX, +@@ -931,6 +959,9 @@ static int plda_init_interrupts(struct p + int i, intx_irq, msi_irq, event_irq; + int ret; + ++ if (!port->event_ops) ++ port->event_ops = &plda_event_ops; ++ + ret = plda_pcie_init_irq_domains(port); + if (ret) { + dev_err(dev, "failed creating IRQ domains\n"); +@@ -1007,6 +1038,8 @@ static int mc_platform_init(struct pci_c + if (ret) + return ret; + ++ port->plda.event_ops = &mc_event_ops; ++ + /* Address translation is up; safe to enable interrupts */ + ret = plda_init_interrupts(pdev, &port->plda, &mc_event); + if (ret) +--- a/drivers/pci/controller/plda/pcie-plda.h ++++ b/drivers/pci/controller/plda/pcie-plda.h +@@ -58,6 +58,7 @@ + #define PM_MSI_INT_EVENTS_SHIFT 30 + #define PM_MSI_INT_SYS_ERR_MASK 0x80000000u + #define PM_MSI_INT_SYS_ERR_SHIFT 31 ++#define SYS_AND_MSI_MASK GENMASK(31, 28) + #define NUM_LOCAL_EVENTS 15 + #define ISTATUS_LOCAL 0x184 + #define IMASK_HOST 0x188 +@@ -108,6 +109,36 @@ enum plda_int_event { + + #define PLDA_MAX_INT_NUM (PLDA_NUM_DMA_EVENTS + PLDA_INT_EVENT_NUM) + ++/* ++ * PLDA interrupt register ++ * ++ * 31 27 23 15 7 0 ++ * +--+--+--+-+------+-+-+-+-+-+-+-+-+-----------+-----------+ ++ * |12|11|10|9| intx |7|6|5|4|3|2|1|0| DMA error | DMA end | ++ * +--+--+--+-+------+-+-+-+-+-+-+-+-+-----------+-----------+ ++ * bit 0-7 DMA interrupt end : reserved for vendor implement ++ * bit 8-15 DMA error : reserved for vendor implement ++ * 0: AXI post error (PLDA_AXI_POST_ERR) ++ * 1: AXI fetch error (PLDA_AXI_FETCH_ERR) ++ * 2: AXI discard error (PLDA_AXI_DISCARD_ERR) ++ * 3: AXI doorbell (PLDA_PCIE_DOORBELL) ++ * 4: PCIe post error (PLDA_PCIE_POST_ERR) ++ * 5: PCIe fetch error (PLDA_PCIE_FETCH_ERR) ++ * 6: PCIe discard error (PLDA_PCIE_DISCARD_ERR) ++ * 7: PCIe doorbell (PLDA_PCIE_DOORBELL) ++ * 8: 4 INTx interruts (PLDA_INTX) ++ * 9: MSI interrupt (PLDA_MSI) ++ * 10: AER event (PLDA_AER_EVENT) ++ * 11: PM/LTR/Hotplug (PLDA_MISC_EVENTS) ++ * 12: System error (PLDA_SYS_ERR) ++ */ ++ ++struct plda_pcie_rp; ++ ++struct plda_event_ops { ++ u32 (*get_events)(struct plda_pcie_rp *pcie); ++}; ++ + struct plda_msi { + struct mutex lock; /* Protect used bitmap */ + struct irq_domain *msi_domain; +@@ -123,6 +154,7 @@ struct plda_pcie_rp { + struct irq_domain *event_domain; + raw_spinlock_t lock; + struct plda_msi msi; ++ const struct plda_event_ops *event_ops; + void __iomem *bridge_addr; + int num_events; + }; diff --git a/target/linux/starfive/patches-6.6/0028-PCI-microchip-Add-event-irqchip-field-to-host-port-a.patch b/target/linux/starfive/patches-6.6/0028-PCI-microchip-Add-event-irqchip-field-to-host-port-a.patch new file mode 100644 index 0000000000..9ed8119b1e --- /dev/null +++ b/target/linux/starfive/patches-6.6/0028-PCI-microchip-Add-event-irqchip-field-to-host-port-a.patch @@ -0,0 +1,144 @@ +From 229ea8e7b674eb5c9bc4f70d43df1bd02a79862a Mon Sep 17 00:00:00 2001 +From: Minda Chen +Date: Mon, 8 Jan 2024 19:06:05 +0800 +Subject: [PATCH 028/116] PCI: microchip: Add event irqchip field to host port + and add PLDA irqchip + +As PLDA dts binding doc(Documentation/devicetree/bindings/pci/ +plda,xpressrich3-axi-common.yaml) showes, PLDA PCIe contains an interrupt +controller. + +Microchip PolarFire PCIE event IRQs includes PLDA interrupts and +Polarfire their own interrupts. The interrupt irqchip ops includes +ack/mask/unmask interrupt ops, which will write correct registers. +Microchip Polarfire PCIe additional interrupts require to write Polarfire +SoC self-defined registers. So Microchip PCIe event irqchip ops can not +be re-used. + +To support PLDA its own event IRQ process, implements PLDA irqchip ops and +add event irqchip field to struct pcie_plda_rp. + +Signed-off-by: Minda Chen +Acked-by: Conor Dooley +--- + .../pci/controller/plda/pcie-microchip-host.c | 66 ++++++++++++++++++- + drivers/pci/controller/plda/pcie-plda.h | 5 +- + 2 files changed, 69 insertions(+), 2 deletions(-) + +--- a/drivers/pci/controller/plda/pcie-microchip-host.c ++++ b/drivers/pci/controller/plda/pcie-microchip-host.c +@@ -770,6 +770,64 @@ static struct irq_chip mc_event_irq_chip + .irq_unmask = mc_unmask_event_irq, + }; + ++static u32 plda_hwirq_to_mask(int hwirq) ++{ ++ u32 mask; ++ ++ /* hwirq 23 - 0 are the same with register */ ++ if (hwirq < EVENT_PM_MSI_INT_INTX) ++ mask = BIT(hwirq); ++ else if (hwirq == EVENT_PM_MSI_INT_INTX) ++ mask = PM_MSI_INT_INTX_MASK; ++ else ++ mask = BIT(hwirq + PCI_NUM_INTX - 1); ++ ++ return mask; ++} ++ ++static void plda_ack_event_irq(struct irq_data *data) ++{ ++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); ++ ++ writel_relaxed(plda_hwirq_to_mask(data->hwirq), ++ port->bridge_addr + ISTATUS_LOCAL); ++} ++ ++static void plda_mask_event_irq(struct irq_data *data) ++{ ++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); ++ u32 mask, val; ++ ++ mask = plda_hwirq_to_mask(data->hwirq); ++ ++ raw_spin_lock(&port->lock); ++ val = readl_relaxed(port->bridge_addr + IMASK_LOCAL); ++ val &= ~mask; ++ writel_relaxed(val, port->bridge_addr + IMASK_LOCAL); ++ raw_spin_unlock(&port->lock); ++} ++ ++static void plda_unmask_event_irq(struct irq_data *data) ++{ ++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); ++ u32 mask, val; ++ ++ mask = plda_hwirq_to_mask(data->hwirq); ++ ++ raw_spin_lock(&port->lock); ++ val = readl_relaxed(port->bridge_addr + IMASK_LOCAL); ++ val |= mask; ++ writel_relaxed(val, port->bridge_addr + IMASK_LOCAL); ++ raw_spin_unlock(&port->lock); ++} ++ ++static struct irq_chip plda_event_irq_chip = { ++ .name = "PLDA PCIe EVENT", ++ .irq_ack = plda_ack_event_irq, ++ .irq_mask = plda_mask_event_irq, ++ .irq_unmask = plda_unmask_event_irq, ++}; ++ + static const struct plda_event_ops plda_event_ops = { + .get_events = plda_get_events, + }; +@@ -777,7 +835,9 @@ static const struct plda_event_ops plda_ + static int plda_pcie_event_map(struct irq_domain *domain, unsigned int irq, + irq_hw_number_t hwirq) + { +- irq_set_chip_and_handler(irq, &mc_event_irq_chip, handle_level_irq); ++ struct plda_pcie_rp *port = (void *)domain->host_data; ++ ++ irq_set_chip_and_handler(irq, port->event_irq_chip, handle_level_irq); + irq_set_chip_data(irq, domain->host_data); + + return 0; +@@ -962,6 +1022,9 @@ static int plda_init_interrupts(struct p + if (!port->event_ops) + port->event_ops = &plda_event_ops; + ++ if (!port->event_irq_chip) ++ port->event_irq_chip = &plda_event_irq_chip; ++ + ret = plda_pcie_init_irq_domains(port); + if (ret) { + dev_err(dev, "failed creating IRQ domains\n"); +@@ -1039,6 +1102,7 @@ static int mc_platform_init(struct pci_c + return ret; + + port->plda.event_ops = &mc_event_ops; ++ port->plda.event_irq_chip = &mc_event_irq_chip; + + /* Address translation is up; safe to enable interrupts */ + ret = plda_init_interrupts(pdev, &port->plda, &mc_event); +--- a/drivers/pci/controller/plda/pcie-plda.h ++++ b/drivers/pci/controller/plda/pcie-plda.h +@@ -107,7 +107,9 @@ enum plda_int_event { + + #define PLDA_NUM_DMA_EVENTS 16 + +-#define PLDA_MAX_INT_NUM (PLDA_NUM_DMA_EVENTS + PLDA_INT_EVENT_NUM) ++#define EVENT_PM_MSI_INT_INTX (PLDA_NUM_DMA_EVENTS + PLDA_INTX) ++#define EVENT_PM_MSI_INT_MSI (PLDA_NUM_DMA_EVENTS + PLDA_MSI) ++#define PLDA_MAX_EVENT_NUM (PLDA_NUM_DMA_EVENTS + PLDA_INT_EVENT_NUM) + + /* + * PLDA interrupt register +@@ -155,6 +157,7 @@ struct plda_pcie_rp { + raw_spinlock_t lock; + struct plda_msi msi; + const struct plda_event_ops *event_ops; ++ const struct irq_chip *event_irq_chip; + void __iomem *bridge_addr; + int num_events; + }; diff --git a/target/linux/starfive/patches-6.6/0029-PCI-microchip-Move-IRQ-functions-to-pcie-plda-host.c.patch b/target/linux/starfive/patches-6.6/0029-PCI-microchip-Move-IRQ-functions-to-pcie-plda-host.c.patch new file mode 100644 index 0000000000..840e334d20 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0029-PCI-microchip-Move-IRQ-functions-to-pcie-plda-host.c.patch @@ -0,0 +1,1028 @@ +From 6be452d8e61594790ae57b282a612ec0df473e61 Mon Sep 17 00:00:00 2001 +From: Minda Chen +Date: Mon, 8 Jan 2024 19:06:06 +0800 +Subject: [PATCH 029/116] PCI: microchip: Move IRQ functions to + pcie-plda-host.c + +Move IRQ related functions to pcie-plda-host.c for re-use these codes. +Now Refactoring codes complete. + +Including MSI, INTx, event interrupts and IRQ init functions. + +Signed-off-by: Minda Chen +Acked-by: Conor Dooley +--- + .../pci/controller/plda/pcie-microchip-host.c | 467 ----------------- + drivers/pci/controller/plda/pcie-plda-host.c | 472 ++++++++++++++++++ + drivers/pci/controller/plda/pcie-plda.h | 3 + + 3 files changed, 475 insertions(+), 467 deletions(-) + +--- a/drivers/pci/controller/plda/pcie-microchip-host.c ++++ b/drivers/pci/controller/plda/pcie-microchip-host.c +@@ -318,244 +318,6 @@ static void mc_pcie_enable_msi(struct mc + ecam + MC_MSI_CAP_CTRL_OFFSET + PCI_MSI_ADDRESS_HI); + } + +-static void plda_handle_msi(struct irq_desc *desc) +-{ +- struct plda_pcie_rp *port = irq_desc_get_handler_data(desc); +- struct irq_chip *chip = irq_desc_get_chip(desc); +- struct device *dev = port->dev; +- struct plda_msi *msi = &port->msi; +- void __iomem *bridge_base_addr = port->bridge_addr; +- unsigned long status; +- u32 bit; +- int ret; +- +- chained_irq_enter(chip, desc); +- +- status = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL); +- if (status & PM_MSI_INT_MSI_MASK) { +- writel_relaxed(status & PM_MSI_INT_MSI_MASK, bridge_base_addr + ISTATUS_LOCAL); +- status = readl_relaxed(bridge_base_addr + ISTATUS_MSI); +- for_each_set_bit(bit, &status, msi->num_vectors) { +- ret = generic_handle_domain_irq(msi->dev_domain, bit); +- if (ret) +- dev_err_ratelimited(dev, "bad MSI IRQ %d\n", +- bit); +- } +- } +- +- chained_irq_exit(chip, desc); +-} +- +-static void plda_msi_bottom_irq_ack(struct irq_data *data) +-{ +- struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); +- void __iomem *bridge_base_addr = port->bridge_addr; +- u32 bitpos = data->hwirq; +- +- writel_relaxed(BIT(bitpos), bridge_base_addr + ISTATUS_MSI); +-} +- +-static void plda_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) +-{ +- struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); +- phys_addr_t addr = port->msi.vector_phy; +- +- msg->address_lo = lower_32_bits(addr); +- msg->address_hi = upper_32_bits(addr); +- msg->data = data->hwirq; +- +- dev_dbg(port->dev, "msi#%x address_hi %#x address_lo %#x\n", +- (int)data->hwirq, msg->address_hi, msg->address_lo); +-} +- +-static int plda_msi_set_affinity(struct irq_data *irq_data, +- const struct cpumask *mask, bool force) +-{ +- return -EINVAL; +-} +- +-static struct irq_chip plda_msi_bottom_irq_chip = { +- .name = "PLDA MSI", +- .irq_ack = plda_msi_bottom_irq_ack, +- .irq_compose_msi_msg = plda_compose_msi_msg, +- .irq_set_affinity = plda_msi_set_affinity, +-}; +- +-static int plda_irq_msi_domain_alloc(struct irq_domain *domain, +- unsigned int virq, +- unsigned int nr_irqs, +- void *args) +-{ +- struct plda_pcie_rp *port = domain->host_data; +- struct plda_msi *msi = &port->msi; +- unsigned long bit; +- +- mutex_lock(&msi->lock); +- bit = find_first_zero_bit(msi->used, msi->num_vectors); +- if (bit >= msi->num_vectors) { +- mutex_unlock(&msi->lock); +- return -ENOSPC; +- } +- +- set_bit(bit, msi->used); +- +- irq_domain_set_info(domain, virq, bit, &plda_msi_bottom_irq_chip, +- domain->host_data, handle_edge_irq, NULL, NULL); +- +- mutex_unlock(&msi->lock); +- +- return 0; +-} +- +-static void plda_irq_msi_domain_free(struct irq_domain *domain, +- unsigned int virq, +- unsigned int nr_irqs) +-{ +- struct irq_data *d = irq_domain_get_irq_data(domain, virq); +- struct plda_pcie_rp *port = irq_data_get_irq_chip_data(d); +- struct plda_msi *msi = &port->msi; +- +- mutex_lock(&msi->lock); +- +- if (test_bit(d->hwirq, msi->used)) +- __clear_bit(d->hwirq, msi->used); +- else +- dev_err(port->dev, "trying to free unused MSI%lu\n", d->hwirq); +- +- mutex_unlock(&msi->lock); +-} +- +-static const struct irq_domain_ops msi_domain_ops = { +- .alloc = plda_irq_msi_domain_alloc, +- .free = plda_irq_msi_domain_free, +-}; +- +-static struct irq_chip plda_msi_irq_chip = { +- .name = "PLDA PCIe MSI", +- .irq_ack = irq_chip_ack_parent, +- .irq_mask = pci_msi_mask_irq, +- .irq_unmask = pci_msi_unmask_irq, +-}; +- +-static struct msi_domain_info plda_msi_domain_info = { +- .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | +- MSI_FLAG_PCI_MSIX), +- .chip = &plda_msi_irq_chip, +-}; +- +-static int plda_allocate_msi_domains(struct plda_pcie_rp *port) +-{ +- struct device *dev = port->dev; +- struct fwnode_handle *fwnode = of_node_to_fwnode(dev->of_node); +- struct plda_msi *msi = &port->msi; +- +- mutex_init(&port->msi.lock); +- +- msi->dev_domain = irq_domain_add_linear(NULL, msi->num_vectors, +- &msi_domain_ops, port); +- if (!msi->dev_domain) { +- dev_err(dev, "failed to create IRQ domain\n"); +- return -ENOMEM; +- } +- +- msi->msi_domain = pci_msi_create_irq_domain(fwnode, +- &plda_msi_domain_info, +- msi->dev_domain); +- if (!msi->msi_domain) { +- dev_err(dev, "failed to create MSI domain\n"); +- irq_domain_remove(msi->dev_domain); +- return -ENOMEM; +- } +- +- return 0; +-} +- +-static void plda_handle_intx(struct irq_desc *desc) +-{ +- struct plda_pcie_rp *port = irq_desc_get_handler_data(desc); +- struct irq_chip *chip = irq_desc_get_chip(desc); +- struct device *dev = port->dev; +- void __iomem *bridge_base_addr = port->bridge_addr; +- unsigned long status; +- u32 bit; +- int ret; +- +- chained_irq_enter(chip, desc); +- +- status = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL); +- if (status & PM_MSI_INT_INTX_MASK) { +- status &= PM_MSI_INT_INTX_MASK; +- status >>= PM_MSI_INT_INTX_SHIFT; +- for_each_set_bit(bit, &status, PCI_NUM_INTX) { +- ret = generic_handle_domain_irq(port->intx_domain, bit); +- if (ret) +- dev_err_ratelimited(dev, "bad INTx IRQ %d\n", +- bit); +- } +- } +- +- chained_irq_exit(chip, desc); +-} +- +-static void plda_ack_intx_irq(struct irq_data *data) +-{ +- struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); +- void __iomem *bridge_base_addr = port->bridge_addr; +- u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT); +- +- writel_relaxed(mask, bridge_base_addr + ISTATUS_LOCAL); +-} +- +-static void plda_mask_intx_irq(struct irq_data *data) +-{ +- struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); +- void __iomem *bridge_base_addr = port->bridge_addr; +- unsigned long flags; +- u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT); +- u32 val; +- +- raw_spin_lock_irqsave(&port->lock, flags); +- val = readl_relaxed(bridge_base_addr + IMASK_LOCAL); +- val &= ~mask; +- writel_relaxed(val, bridge_base_addr + IMASK_LOCAL); +- raw_spin_unlock_irqrestore(&port->lock, flags); +-} +- +-static void plda_unmask_intx_irq(struct irq_data *data) +-{ +- struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); +- void __iomem *bridge_base_addr = port->bridge_addr; +- unsigned long flags; +- u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT); +- u32 val; +- +- raw_spin_lock_irqsave(&port->lock, flags); +- val = readl_relaxed(bridge_base_addr + IMASK_LOCAL); +- val |= mask; +- writel_relaxed(val, bridge_base_addr + IMASK_LOCAL); +- raw_spin_unlock_irqrestore(&port->lock, flags); +-} +- +-static struct irq_chip plda_intx_irq_chip = { +- .name = "PLDA PCIe INTx", +- .irq_ack = plda_ack_intx_irq, +- .irq_mask = plda_mask_intx_irq, +- .irq_unmask = plda_unmask_intx_irq, +-}; +- +-static int plda_pcie_intx_map(struct irq_domain *domain, unsigned int irq, +- irq_hw_number_t hwirq) +-{ +- irq_set_chip_and_handler(irq, &plda_intx_irq_chip, handle_level_irq); +- irq_set_chip_data(irq, domain->host_data); +- +- return 0; +-} +- +-static const struct irq_domain_ops intx_domain_ops = { +- .map = plda_pcie_intx_map, +-}; +- + static inline u32 reg_to_event(u32 reg, struct event_map field) + { + return (reg & field.reg_mask) ? BIT(field.event_bit) : 0; +@@ -626,26 +388,6 @@ static u32 mc_get_events(struct plda_pci + return events; + } + +-static u32 plda_get_events(struct plda_pcie_rp *port) +-{ +- u32 events, val, origin; +- +- origin = readl_relaxed(port->bridge_addr + ISTATUS_LOCAL); +- +- /* MSI event and sys events */ +- val = (origin & SYS_AND_MSI_MASK) >> PM_MSI_INT_MSI_SHIFT; +- events = val << (PM_MSI_INT_MSI_SHIFT - PCI_NUM_INTX + 1); +- +- /* INTx events */ +- if (origin & PM_MSI_INT_INTX_MASK) +- events |= BIT(PM_MSI_INT_INTX_SHIFT); +- +- /* remains are same with register */ +- events |= origin & GENMASK(P_ATR_EVT_DOORBELL_SHIFT, 0); +- +- return events; +-} +- + static irqreturn_t mc_event_handler(int irq, void *dev_id) + { + struct plda_pcie_rp *port = dev_id; +@@ -662,28 +404,6 @@ static irqreturn_t mc_event_handler(int + return IRQ_HANDLED; + } + +-static irqreturn_t plda_event_handler(int irq, void *dev_id) +-{ +- return IRQ_HANDLED; +-} +- +-static void plda_handle_event(struct irq_desc *desc) +-{ +- struct plda_pcie_rp *port = irq_desc_get_handler_data(desc); +- unsigned long events; +- u32 bit; +- struct irq_chip *chip = irq_desc_get_chip(desc); +- +- chained_irq_enter(chip, desc); +- +- events = port->event_ops->get_events(port); +- +- for_each_set_bit(bit, &events, port->num_events) +- generic_handle_domain_irq(port->event_domain, bit); +- +- chained_irq_exit(chip, desc); +-} +- + static void mc_ack_event_irq(struct irq_data *data) + { + struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); +@@ -770,83 +490,6 @@ static struct irq_chip mc_event_irq_chip + .irq_unmask = mc_unmask_event_irq, + }; + +-static u32 plda_hwirq_to_mask(int hwirq) +-{ +- u32 mask; +- +- /* hwirq 23 - 0 are the same with register */ +- if (hwirq < EVENT_PM_MSI_INT_INTX) +- mask = BIT(hwirq); +- else if (hwirq == EVENT_PM_MSI_INT_INTX) +- mask = PM_MSI_INT_INTX_MASK; +- else +- mask = BIT(hwirq + PCI_NUM_INTX - 1); +- +- return mask; +-} +- +-static void plda_ack_event_irq(struct irq_data *data) +-{ +- struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); +- +- writel_relaxed(plda_hwirq_to_mask(data->hwirq), +- port->bridge_addr + ISTATUS_LOCAL); +-} +- +-static void plda_mask_event_irq(struct irq_data *data) +-{ +- struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); +- u32 mask, val; +- +- mask = plda_hwirq_to_mask(data->hwirq); +- +- raw_spin_lock(&port->lock); +- val = readl_relaxed(port->bridge_addr + IMASK_LOCAL); +- val &= ~mask; +- writel_relaxed(val, port->bridge_addr + IMASK_LOCAL); +- raw_spin_unlock(&port->lock); +-} +- +-static void plda_unmask_event_irq(struct irq_data *data) +-{ +- struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); +- u32 mask, val; +- +- mask = plda_hwirq_to_mask(data->hwirq); +- +- raw_spin_lock(&port->lock); +- val = readl_relaxed(port->bridge_addr + IMASK_LOCAL); +- val |= mask; +- writel_relaxed(val, port->bridge_addr + IMASK_LOCAL); +- raw_spin_unlock(&port->lock); +-} +- +-static struct irq_chip plda_event_irq_chip = { +- .name = "PLDA PCIe EVENT", +- .irq_ack = plda_ack_event_irq, +- .irq_mask = plda_mask_event_irq, +- .irq_unmask = plda_unmask_event_irq, +-}; +- +-static const struct plda_event_ops plda_event_ops = { +- .get_events = plda_get_events, +-}; +- +-static int plda_pcie_event_map(struct irq_domain *domain, unsigned int irq, +- irq_hw_number_t hwirq) +-{ +- struct plda_pcie_rp *port = (void *)domain->host_data; +- +- irq_set_chip_and_handler(irq, port->event_irq_chip, handle_level_irq); +- irq_set_chip_data(irq, domain->host_data); +- +- return 0; +-} +- +-static const struct irq_domain_ops plda_event_domain_ops = { +- .map = plda_pcie_event_map, +-}; +- + static inline void mc_pcie_deinit_clk(void *data) + { + struct clk *clk = data; +@@ -909,47 +552,6 @@ static const struct plda_event mc_event + .msi_event = EVENT_LOCAL_PM_MSI_INT_MSI, + }; + +-static int plda_pcie_init_irq_domains(struct plda_pcie_rp *port) +-{ +- struct device *dev = port->dev; +- struct device_node *node = dev->of_node; +- struct device_node *pcie_intc_node; +- +- /* Setup INTx */ +- pcie_intc_node = of_get_next_child(node, NULL); +- if (!pcie_intc_node) { +- dev_err(dev, "failed to find PCIe Intc node\n"); +- return -EINVAL; +- } +- +- port->event_domain = irq_domain_add_linear(pcie_intc_node, +- port->num_events, +- &plda_event_domain_ops, +- port); +- if (!port->event_domain) { +- dev_err(dev, "failed to get event domain\n"); +- of_node_put(pcie_intc_node); +- return -ENOMEM; +- } +- +- irq_domain_update_bus_token(port->event_domain, DOMAIN_BUS_NEXUS); +- +- port->intx_domain = irq_domain_add_linear(pcie_intc_node, PCI_NUM_INTX, +- &intx_domain_ops, port); +- if (!port->intx_domain) { +- dev_err(dev, "failed to get an INTx IRQ domain\n"); +- of_node_put(pcie_intc_node); +- return -ENOMEM; +- } +- +- irq_domain_update_bus_token(port->intx_domain, DOMAIN_BUS_WIRED); +- +- of_node_put(pcie_intc_node); +- raw_spin_lock_init(&port->lock); +- +- return plda_allocate_msi_domains(port); +-} +- + static inline void mc_clear_secs(struct mc_pcie *port) + { + void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR; +@@ -1010,75 +612,6 @@ static void mc_disable_interrupts(struct + writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_HOST); + } + +-static int plda_init_interrupts(struct platform_device *pdev, +- struct plda_pcie_rp *port, +- const struct plda_event *event) +-{ +- struct device *dev = &pdev->dev; +- int irq; +- int i, intx_irq, msi_irq, event_irq; +- int ret; +- +- if (!port->event_ops) +- port->event_ops = &plda_event_ops; +- +- if (!port->event_irq_chip) +- port->event_irq_chip = &plda_event_irq_chip; +- +- ret = plda_pcie_init_irq_domains(port); +- if (ret) { +- dev_err(dev, "failed creating IRQ domains\n"); +- return ret; +- } +- +- irq = platform_get_irq(pdev, 0); +- if (irq < 0) +- return -ENODEV; +- +- for (i = 0; i < port->num_events; i++) { +- event_irq = irq_create_mapping(port->event_domain, i); +- if (!event_irq) { +- dev_err(dev, "failed to map hwirq %d\n", i); +- return -ENXIO; +- } +- +- if (event->request_event_irq) +- ret = event->request_event_irq(port, event_irq, i); +- else +- ret = devm_request_irq(dev, event_irq, +- plda_event_handler, +- 0, NULL, port); +- +- if (ret) { +- dev_err(dev, "failed to request IRQ %d\n", event_irq); +- return ret; +- } +- } +- +- intx_irq = irq_create_mapping(port->event_domain, +- event->intx_event); +- if (!intx_irq) { +- dev_err(dev, "failed to map INTx interrupt\n"); +- return -ENXIO; +- } +- +- /* Plug the INTx chained handler */ +- irq_set_chained_handler_and_data(intx_irq, plda_handle_intx, port); +- +- msi_irq = irq_create_mapping(port->event_domain, +- event->msi_event); +- if (!msi_irq) +- return -ENXIO; +- +- /* Plug the MSI chained handler */ +- irq_set_chained_handler_and_data(msi_irq, plda_handle_msi, port); +- +- /* Plug the main event chained handler */ +- irq_set_chained_handler_and_data(irq, plda_handle_event, port); +- +- return 0; +-} +- + static int mc_platform_init(struct pci_config_window *cfg) + { + struct device *dev = cfg->parent; +--- a/drivers/pci/controller/plda/pcie-plda-host.c ++++ b/drivers/pci/controller/plda/pcie-plda-host.c +@@ -7,11 +7,483 @@ + * Author: Daire McNamara + */ + ++#include ++#include ++#include + #include + #include + + #include "pcie-plda.h" + ++static void plda_handle_msi(struct irq_desc *desc) ++{ ++ struct plda_pcie_rp *port = irq_desc_get_handler_data(desc); ++ struct irq_chip *chip = irq_desc_get_chip(desc); ++ struct device *dev = port->dev; ++ struct plda_msi *msi = &port->msi; ++ void __iomem *bridge_base_addr = port->bridge_addr; ++ unsigned long status; ++ u32 bit; ++ int ret; ++ ++ chained_irq_enter(chip, desc); ++ ++ status = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL); ++ if (status & PM_MSI_INT_MSI_MASK) { ++ writel_relaxed(status & PM_MSI_INT_MSI_MASK, ++ bridge_base_addr + ISTATUS_LOCAL); ++ status = readl_relaxed(bridge_base_addr + ISTATUS_MSI); ++ for_each_set_bit(bit, &status, msi->num_vectors) { ++ ret = generic_handle_domain_irq(msi->dev_domain, bit); ++ if (ret) ++ dev_err_ratelimited(dev, "bad MSI IRQ %d\n", ++ bit); ++ } ++ } ++ ++ chained_irq_exit(chip, desc); ++} ++ ++static void plda_msi_bottom_irq_ack(struct irq_data *data) ++{ ++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); ++ void __iomem *bridge_base_addr = port->bridge_addr; ++ u32 bitpos = data->hwirq; ++ ++ writel_relaxed(BIT(bitpos), bridge_base_addr + ISTATUS_MSI); ++} ++ ++static void plda_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) ++{ ++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); ++ phys_addr_t addr = port->msi.vector_phy; ++ ++ msg->address_lo = lower_32_bits(addr); ++ msg->address_hi = upper_32_bits(addr); ++ msg->data = data->hwirq; ++ ++ dev_dbg(port->dev, "msi#%x address_hi %#x address_lo %#x\n", ++ (int)data->hwirq, msg->address_hi, msg->address_lo); ++} ++ ++static int plda_msi_set_affinity(struct irq_data *irq_data, ++ const struct cpumask *mask, bool force) ++{ ++ return -EINVAL; ++} ++ ++static struct irq_chip plda_msi_bottom_irq_chip = { ++ .name = "PLDA MSI", ++ .irq_ack = plda_msi_bottom_irq_ack, ++ .irq_compose_msi_msg = plda_compose_msi_msg, ++ .irq_set_affinity = plda_msi_set_affinity, ++}; ++ ++static int plda_irq_msi_domain_alloc(struct irq_domain *domain, ++ unsigned int virq, ++ unsigned int nr_irqs, ++ void *args) ++{ ++ struct plda_pcie_rp *port = domain->host_data; ++ struct plda_msi *msi = &port->msi; ++ unsigned long bit; ++ ++ mutex_lock(&msi->lock); ++ bit = find_first_zero_bit(msi->used, msi->num_vectors); ++ if (bit >= msi->num_vectors) { ++ mutex_unlock(&msi->lock); ++ return -ENOSPC; ++ } ++ ++ set_bit(bit, msi->used); ++ ++ irq_domain_set_info(domain, virq, bit, &plda_msi_bottom_irq_chip, ++ domain->host_data, handle_edge_irq, NULL, NULL); ++ ++ mutex_unlock(&msi->lock); ++ ++ return 0; ++} ++ ++static void plda_irq_msi_domain_free(struct irq_domain *domain, ++ unsigned int virq, ++ unsigned int nr_irqs) ++{ ++ struct irq_data *d = irq_domain_get_irq_data(domain, virq); ++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(d); ++ struct plda_msi *msi = &port->msi; ++ ++ mutex_lock(&msi->lock); ++ ++ if (test_bit(d->hwirq, msi->used)) ++ __clear_bit(d->hwirq, msi->used); ++ else ++ dev_err(port->dev, "trying to free unused MSI%lu\n", d->hwirq); ++ ++ mutex_unlock(&msi->lock); ++} ++ ++static const struct irq_domain_ops msi_domain_ops = { ++ .alloc = plda_irq_msi_domain_alloc, ++ .free = plda_irq_msi_domain_free, ++}; ++ ++static struct irq_chip plda_msi_irq_chip = { ++ .name = "PLDA PCIe MSI", ++ .irq_ack = irq_chip_ack_parent, ++ .irq_mask = pci_msi_mask_irq, ++ .irq_unmask = pci_msi_unmask_irq, ++}; ++ ++static struct msi_domain_info plda_msi_domain_info = { ++ .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | ++ MSI_FLAG_PCI_MSIX), ++ .chip = &plda_msi_irq_chip, ++}; ++ ++static int plda_allocate_msi_domains(struct plda_pcie_rp *port) ++{ ++ struct device *dev = port->dev; ++ struct fwnode_handle *fwnode = of_node_to_fwnode(dev->of_node); ++ struct plda_msi *msi = &port->msi; ++ ++ mutex_init(&port->msi.lock); ++ ++ msi->dev_domain = irq_domain_add_linear(NULL, msi->num_vectors, ++ &msi_domain_ops, port); ++ if (!msi->dev_domain) { ++ dev_err(dev, "failed to create IRQ domain\n"); ++ return -ENOMEM; ++ } ++ ++ msi->msi_domain = pci_msi_create_irq_domain(fwnode, ++ &plda_msi_domain_info, ++ msi->dev_domain); ++ if (!msi->msi_domain) { ++ dev_err(dev, "failed to create MSI domain\n"); ++ irq_domain_remove(msi->dev_domain); ++ return -ENOMEM; ++ } ++ ++ return 0; ++} ++ ++static void plda_handle_intx(struct irq_desc *desc) ++{ ++ struct plda_pcie_rp *port = irq_desc_get_handler_data(desc); ++ struct irq_chip *chip = irq_desc_get_chip(desc); ++ struct device *dev = port->dev; ++ void __iomem *bridge_base_addr = port->bridge_addr; ++ unsigned long status; ++ u32 bit; ++ int ret; ++ ++ chained_irq_enter(chip, desc); ++ ++ status = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL); ++ if (status & PM_MSI_INT_INTX_MASK) { ++ status &= PM_MSI_INT_INTX_MASK; ++ status >>= PM_MSI_INT_INTX_SHIFT; ++ for_each_set_bit(bit, &status, PCI_NUM_INTX) { ++ ret = generic_handle_domain_irq(port->intx_domain, bit); ++ if (ret) ++ dev_err_ratelimited(dev, "bad INTx IRQ %d\n", ++ bit); ++ } ++ } ++ ++ chained_irq_exit(chip, desc); ++} ++ ++static void plda_ack_intx_irq(struct irq_data *data) ++{ ++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); ++ void __iomem *bridge_base_addr = port->bridge_addr; ++ u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT); ++ ++ writel_relaxed(mask, bridge_base_addr + ISTATUS_LOCAL); ++} ++ ++static void plda_mask_intx_irq(struct irq_data *data) ++{ ++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); ++ void __iomem *bridge_base_addr = port->bridge_addr; ++ unsigned long flags; ++ u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT); ++ u32 val; ++ ++ raw_spin_lock_irqsave(&port->lock, flags); ++ val = readl_relaxed(bridge_base_addr + IMASK_LOCAL); ++ val &= ~mask; ++ writel_relaxed(val, bridge_base_addr + IMASK_LOCAL); ++ raw_spin_unlock_irqrestore(&port->lock, flags); ++} ++ ++static void plda_unmask_intx_irq(struct irq_data *data) ++{ ++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); ++ void __iomem *bridge_base_addr = port->bridge_addr; ++ unsigned long flags; ++ u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT); ++ u32 val; ++ ++ raw_spin_lock_irqsave(&port->lock, flags); ++ val = readl_relaxed(bridge_base_addr + IMASK_LOCAL); ++ val |= mask; ++ writel_relaxed(val, bridge_base_addr + IMASK_LOCAL); ++ raw_spin_unlock_irqrestore(&port->lock, flags); ++} ++ ++static struct irq_chip plda_intx_irq_chip = { ++ .name = "PLDA PCIe INTx", ++ .irq_ack = plda_ack_intx_irq, ++ .irq_mask = plda_mask_intx_irq, ++ .irq_unmask = plda_unmask_intx_irq, ++}; ++ ++static int plda_pcie_intx_map(struct irq_domain *domain, unsigned int irq, ++ irq_hw_number_t hwirq) ++{ ++ irq_set_chip_and_handler(irq, &plda_intx_irq_chip, handle_level_irq); ++ irq_set_chip_data(irq, domain->host_data); ++ ++ return 0; ++} ++ ++static const struct irq_domain_ops intx_domain_ops = { ++ .map = plda_pcie_intx_map, ++}; ++ ++static u32 plda_get_events(struct plda_pcie_rp *port) ++{ ++ u32 events, val, origin; ++ ++ origin = readl_relaxed(port->bridge_addr + ISTATUS_LOCAL); ++ ++ /* MSI event and sys events */ ++ val = (origin & SYS_AND_MSI_MASK) >> PM_MSI_INT_MSI_SHIFT; ++ events = val << (PM_MSI_INT_MSI_SHIFT - PCI_NUM_INTX + 1); ++ ++ /* INTx events */ ++ if (origin & PM_MSI_INT_INTX_MASK) ++ events |= BIT(PM_MSI_INT_INTX_SHIFT); ++ ++ /* remains are same with register */ ++ events |= origin & GENMASK(P_ATR_EVT_DOORBELL_SHIFT, 0); ++ ++ return events; ++} ++ ++static irqreturn_t plda_event_handler(int irq, void *dev_id) ++{ ++ return IRQ_HANDLED; ++} ++ ++static void plda_handle_event(struct irq_desc *desc) ++{ ++ struct plda_pcie_rp *port = irq_desc_get_handler_data(desc); ++ unsigned long events; ++ u32 bit; ++ struct irq_chip *chip = irq_desc_get_chip(desc); ++ ++ chained_irq_enter(chip, desc); ++ ++ events = port->event_ops->get_events(port); ++ ++ for_each_set_bit(bit, &events, port->num_events) ++ generic_handle_domain_irq(port->event_domain, bit); ++ ++ chained_irq_exit(chip, desc); ++} ++ ++static u32 plda_hwirq_to_mask(int hwirq) ++{ ++ u32 mask; ++ ++ /* hwirq 23 - 0 are the same with register */ ++ if (hwirq < EVENT_PM_MSI_INT_INTX) ++ mask = BIT(hwirq); ++ else if (hwirq == EVENT_PM_MSI_INT_INTX) ++ mask = PM_MSI_INT_INTX_MASK; ++ else ++ mask = BIT(hwirq + PCI_NUM_INTX - 1); ++ ++ return mask; ++} ++ ++static void plda_ack_event_irq(struct irq_data *data) ++{ ++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); ++ ++ writel_relaxed(plda_hwirq_to_mask(data->hwirq), ++ port->bridge_addr + ISTATUS_LOCAL); ++} ++ ++static void plda_mask_event_irq(struct irq_data *data) ++{ ++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); ++ u32 mask, val; ++ ++ mask = plda_hwirq_to_mask(data->hwirq); ++ ++ raw_spin_lock(&port->lock); ++ val = readl_relaxed(port->bridge_addr + IMASK_LOCAL); ++ val &= ~mask; ++ writel_relaxed(val, port->bridge_addr + IMASK_LOCAL); ++ raw_spin_unlock(&port->lock); ++} ++ ++static void plda_unmask_event_irq(struct irq_data *data) ++{ ++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); ++ u32 mask, val; ++ ++ mask = plda_hwirq_to_mask(data->hwirq); ++ ++ raw_spin_lock(&port->lock); ++ val = readl_relaxed(port->bridge_addr + IMASK_LOCAL); ++ val |= mask; ++ writel_relaxed(val, port->bridge_addr + IMASK_LOCAL); ++ raw_spin_unlock(&port->lock); ++} ++ ++static struct irq_chip plda_event_irq_chip = { ++ .name = "PLDA PCIe EVENT", ++ .irq_ack = plda_ack_event_irq, ++ .irq_mask = plda_mask_event_irq, ++ .irq_unmask = plda_unmask_event_irq, ++}; ++ ++static const struct plda_event_ops plda_event_ops = { ++ .get_events = plda_get_events, ++}; ++ ++static int plda_pcie_event_map(struct irq_domain *domain, unsigned int irq, ++ irq_hw_number_t hwirq) ++{ ++ struct plda_pcie_rp *port = (void *)domain->host_data; ++ ++ irq_set_chip_and_handler(irq, port->event_irq_chip, handle_level_irq); ++ irq_set_chip_data(irq, domain->host_data); ++ ++ return 0; ++} ++ ++static const struct irq_domain_ops plda_event_domain_ops = { ++ .map = plda_pcie_event_map, ++}; ++ ++static int plda_pcie_init_irq_domains(struct plda_pcie_rp *port) ++{ ++ struct device *dev = port->dev; ++ struct device_node *node = dev->of_node; ++ struct device_node *pcie_intc_node; ++ ++ /* Setup INTx */ ++ pcie_intc_node = of_get_next_child(node, NULL); ++ if (!pcie_intc_node) { ++ dev_err(dev, "failed to find PCIe Intc node\n"); ++ return -EINVAL; ++ } ++ ++ port->event_domain = irq_domain_add_linear(pcie_intc_node, ++ port->num_events, ++ &plda_event_domain_ops, ++ port); ++ if (!port->event_domain) { ++ dev_err(dev, "failed to get event domain\n"); ++ of_node_put(pcie_intc_node); ++ return -ENOMEM; ++ } ++ ++ irq_domain_update_bus_token(port->event_domain, DOMAIN_BUS_NEXUS); ++ ++ port->intx_domain = irq_domain_add_linear(pcie_intc_node, PCI_NUM_INTX, ++ &intx_domain_ops, port); ++ if (!port->intx_domain) { ++ dev_err(dev, "failed to get an INTx IRQ domain\n"); ++ of_node_put(pcie_intc_node); ++ return -ENOMEM; ++ } ++ ++ irq_domain_update_bus_token(port->intx_domain, DOMAIN_BUS_WIRED); ++ ++ of_node_put(pcie_intc_node); ++ raw_spin_lock_init(&port->lock); ++ ++ return plda_allocate_msi_domains(port); ++} ++ ++int plda_init_interrupts(struct platform_device *pdev, ++ struct plda_pcie_rp *port, ++ const struct plda_event *event) ++{ ++ struct device *dev = &pdev->dev; ++ int irq; ++ int i, intx_irq, msi_irq, event_irq; ++ int ret; ++ ++ if (!port->event_ops) ++ port->event_ops = &plda_event_ops; ++ ++ if (!port->event_irq_chip) ++ port->event_irq_chip = &plda_event_irq_chip; ++ ++ ret = plda_pcie_init_irq_domains(port); ++ if (ret) { ++ dev_err(dev, "failed creating IRQ domains\n"); ++ return ret; ++ } ++ ++ irq = platform_get_irq(pdev, 0); ++ if (irq < 0) ++ return -ENODEV; ++ ++ for (i = 0; i < port->num_events; i++) { ++ event_irq = irq_create_mapping(port->event_domain, i); ++ if (!event_irq) { ++ dev_err(dev, "failed to map hwirq %d\n", i); ++ return -ENXIO; ++ } ++ ++ if (event->request_event_irq) ++ ret = event->request_event_irq(port, event_irq, i); ++ else ++ ret = devm_request_irq(dev, event_irq, ++ plda_event_handler, ++ 0, NULL, port); ++ ++ if (ret) { ++ dev_err(dev, "failed to request IRQ %d\n", event_irq); ++ return ret; ++ } ++ } ++ ++ intx_irq = irq_create_mapping(port->event_domain, ++ event->intx_event); ++ if (!intx_irq) { ++ dev_err(dev, "failed to map INTx interrupt\n"); ++ return -ENXIO; ++ } ++ ++ /* Plug the INTx chained handler */ ++ irq_set_chained_handler_and_data(intx_irq, plda_handle_intx, port); ++ ++ msi_irq = irq_create_mapping(port->event_domain, ++ event->msi_event); ++ if (!msi_irq) ++ return -ENXIO; ++ ++ /* Plug the MSI chained handler */ ++ irq_set_chained_handler_and_data(msi_irq, plda_handle_msi, port); ++ ++ /* Plug the main event chained handler */ ++ irq_set_chained_handler_and_data(irq, plda_handle_event, port); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(plda_init_interrupts); ++ + void plda_pcie_setup_window(void __iomem *bridge_base_addr, u32 index, + phys_addr_t axi_addr, phys_addr_t pci_addr, + size_t size) +--- a/drivers/pci/controller/plda/pcie-plda.h ++++ b/drivers/pci/controller/plda/pcie-plda.h +@@ -169,6 +169,9 @@ struct plda_event { + int msi_event; + }; + ++int plda_init_interrupts(struct platform_device *pdev, ++ struct plda_pcie_rp *port, ++ const struct plda_event *event); + void plda_pcie_setup_window(void __iomem *bridge_base_addr, u32 index, + phys_addr_t axi_addr, phys_addr_t pci_addr, + size_t size); diff --git a/target/linux/starfive/patches-6.6/0030-pci-plda-Add-event-bitmap-field-to-struct-plda_pcie_.patch b/target/linux/starfive/patches-6.6/0030-pci-plda-Add-event-bitmap-field-to-struct-plda_pcie_.patch new file mode 100644 index 0000000000..dcd2310e5e --- /dev/null +++ b/target/linux/starfive/patches-6.6/0030-pci-plda-Add-event-bitmap-field-to-struct-plda_pcie_.patch @@ -0,0 +1,67 @@ +From 142fc300fd7511a217783dcfa342031d8ad70188 Mon Sep 17 00:00:00 2001 +From: Minda Chen +Date: Mon, 8 Jan 2024 19:06:07 +0800 +Subject: [PATCH 030/116] pci: plda: Add event bitmap field to struct + plda_pcie_rp + +For PLDA DMA interrupts are not all implemented. The non-implemented +interrupts should be masked. So add a bitmap field to mask the non- +implemented interrupts. + +Signed-off-by: Minda Chen +--- + drivers/pci/controller/plda/pcie-microchip-host.c | 1 + + drivers/pci/controller/plda/pcie-plda-host.c | 6 ++++-- + drivers/pci/controller/plda/pcie-plda.h | 1 + + 3 files changed, 6 insertions(+), 2 deletions(-) + +--- a/drivers/pci/controller/plda/pcie-microchip-host.c ++++ b/drivers/pci/controller/plda/pcie-microchip-host.c +@@ -636,6 +636,7 @@ static int mc_platform_init(struct pci_c + + port->plda.event_ops = &mc_event_ops; + port->plda.event_irq_chip = &mc_event_irq_chip; ++ port->plda.events_bitmap = GENMASK(NUM_EVENTS - 1, 0); + + /* Address translation is up; safe to enable interrupts */ + ret = plda_init_interrupts(pdev, &port->plda, &mc_event); +--- a/drivers/pci/controller/plda/pcie-plda-host.c ++++ b/drivers/pci/controller/plda/pcie-plda-host.c +@@ -290,6 +290,7 @@ static void plda_handle_event(struct irq + + events = port->event_ops->get_events(port); + ++ events &= port->events_bitmap; + for_each_set_bit(bit, &events, port->num_events) + generic_handle_domain_irq(port->event_domain, bit); + +@@ -420,8 +421,9 @@ int plda_init_interrupts(struct platform + { + struct device *dev = &pdev->dev; + int irq; +- int i, intx_irq, msi_irq, event_irq; ++ int intx_irq, msi_irq, event_irq; + int ret; ++ u32 i; + + if (!port->event_ops) + port->event_ops = &plda_event_ops; +@@ -439,7 +441,7 @@ int plda_init_interrupts(struct platform + if (irq < 0) + return -ENODEV; + +- for (i = 0; i < port->num_events; i++) { ++ for_each_set_bit(i, &port->events_bitmap, port->num_events) { + event_irq = irq_create_mapping(port->event_domain, i); + if (!event_irq) { + dev_err(dev, "failed to map hwirq %d\n", i); +--- a/drivers/pci/controller/plda/pcie-plda.h ++++ b/drivers/pci/controller/plda/pcie-plda.h +@@ -159,6 +159,7 @@ struct plda_pcie_rp { + const struct plda_event_ops *event_ops; + const struct irq_chip *event_irq_chip; + void __iomem *bridge_addr; ++ unsigned long events_bitmap; + int num_events; + }; + diff --git a/target/linux/starfive/patches-6.6/0031-PCI-plda-Add-host-init-deinit-and-map-bus-functions.patch b/target/linux/starfive/patches-6.6/0031-PCI-plda-Add-host-init-deinit-and-map-bus-functions.patch new file mode 100644 index 0000000000..eb22849da6 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0031-PCI-plda-Add-host-init-deinit-and-map-bus-functions.patch @@ -0,0 +1,256 @@ +From 3b9991438094dc472dacb4555603bdc379653411 Mon Sep 17 00:00:00 2001 +From: Minda Chen +Date: Mon, 8 Jan 2024 19:06:08 +0800 +Subject: [PATCH 031/116] PCI: plda: Add host init/deinit and map bus functions + +Add PLDA host plda_pcie_host_init()/plda_pcie_host_deinit() and map bus +function. So vendor can use it to init PLDA PCIe host core. + +Signed-off-by: Minda Chen +Reviewed-by: Mason Huo +--- + drivers/pci/controller/plda/pcie-plda-host.c | 131 +++++++++++++++++-- + drivers/pci/controller/plda/pcie-plda.h | 22 ++++ + 2 files changed, 139 insertions(+), 14 deletions(-) + +--- a/drivers/pci/controller/plda/pcie-plda-host.c ++++ b/drivers/pci/controller/plda/pcie-plda-host.c +@@ -3,6 +3,7 @@ + * PLDA PCIe XpressRich host controller driver + * + * Copyright (C) 2023 Microchip Co. Ltd ++ * StarFive Co. Ltd + * + * Author: Daire McNamara + */ +@@ -15,6 +16,15 @@ + + #include "pcie-plda.h" + ++void __iomem *plda_pcie_map_bus(struct pci_bus *bus, unsigned int devfn, ++ int where) ++{ ++ struct plda_pcie_rp *pcie = bus->sysdata; ++ ++ return pcie->config_base + PCIE_ECAM_OFFSET(bus->number, devfn, where); ++} ++EXPORT_SYMBOL_GPL(plda_pcie_map_bus); ++ + static void plda_handle_msi(struct irq_desc *desc) + { + struct plda_pcie_rp *port = irq_desc_get_handler_data(desc); +@@ -420,9 +430,7 @@ int plda_init_interrupts(struct platform + const struct plda_event *event) + { + struct device *dev = &pdev->dev; +- int irq; +- int intx_irq, msi_irq, event_irq; +- int ret; ++ int event_irq, ret; + u32 i; + + if (!port->event_ops) +@@ -437,8 +445,8 @@ int plda_init_interrupts(struct platform + return ret; + } + +- irq = platform_get_irq(pdev, 0); +- if (irq < 0) ++ port->irq = platform_get_irq(pdev, 0); ++ if (port->irq < 0) + return -ENODEV; + + for_each_set_bit(i, &port->events_bitmap, port->num_events) { +@@ -461,26 +469,26 @@ int plda_init_interrupts(struct platform + } + } + +- intx_irq = irq_create_mapping(port->event_domain, +- event->intx_event); +- if (!intx_irq) { ++ port->intx_irq = irq_create_mapping(port->event_domain, ++ event->intx_event); ++ if (!port->intx_irq) { + dev_err(dev, "failed to map INTx interrupt\n"); + return -ENXIO; + } + + /* Plug the INTx chained handler */ +- irq_set_chained_handler_and_data(intx_irq, plda_handle_intx, port); ++ irq_set_chained_handler_and_data(port->intx_irq, plda_handle_intx, port); + +- msi_irq = irq_create_mapping(port->event_domain, +- event->msi_event); +- if (!msi_irq) ++ port->msi_irq = irq_create_mapping(port->event_domain, ++ event->msi_event); ++ if (!port->msi_irq) + return -ENXIO; + + /* Plug the MSI chained handler */ +- irq_set_chained_handler_and_data(msi_irq, plda_handle_msi, port); ++ irq_set_chained_handler_and_data(port->msi_irq, plda_handle_msi, port); + + /* Plug the main event chained handler */ +- irq_set_chained_handler_and_data(irq, plda_handle_event, port); ++ irq_set_chained_handler_and_data(port->irq, plda_handle_event, port); + + return 0; + } +@@ -546,3 +554,98 @@ int plda_pcie_setup_iomems(struct pci_ho + return 0; + } + EXPORT_SYMBOL_GPL(plda_pcie_setup_iomems); ++ ++static void plda_pcie_irq_domain_deinit(struct plda_pcie_rp *pcie) ++{ ++ irq_set_chained_handler_and_data(pcie->irq, NULL, NULL); ++ irq_set_chained_handler_and_data(pcie->msi_irq, NULL, NULL); ++ irq_set_chained_handler_and_data(pcie->intx_irq, NULL, NULL); ++ ++ irq_domain_remove(pcie->msi.msi_domain); ++ irq_domain_remove(pcie->msi.dev_domain); ++ ++ irq_domain_remove(pcie->intx_domain); ++ irq_domain_remove(pcie->event_domain); ++} ++ ++int plda_pcie_host_init(struct plda_pcie_rp *port, struct pci_ops *ops, ++ const struct plda_event *plda_event) ++{ ++ struct device *dev = port->dev; ++ struct pci_host_bridge *bridge; ++ struct platform_device *pdev = to_platform_device(dev); ++ struct resource *cfg_res; ++ int ret; ++ ++ pdev = to_platform_device(dev); ++ ++ port->bridge_addr = ++ devm_platform_ioremap_resource_byname(pdev, "apb"); ++ ++ if (IS_ERR(port->bridge_addr)) ++ return dev_err_probe(dev, PTR_ERR(port->bridge_addr), ++ "failed to map reg memory\n"); ++ ++ cfg_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg"); ++ if (!cfg_res) ++ return dev_err_probe(dev, -ENODEV, ++ "failed to get config memory\n"); ++ ++ port->config_base = devm_ioremap_resource(dev, cfg_res); ++ if (IS_ERR(port->config_base)) ++ return dev_err_probe(dev, PTR_ERR(port->config_base), ++ "failed to map config memory\n"); ++ ++ bridge = devm_pci_alloc_host_bridge(dev, 0); ++ if (!bridge) ++ return dev_err_probe(dev, -ENOMEM, ++ "failed to alloc bridge\n"); ++ ++ if (port->host_ops && port->host_ops->host_init) { ++ ret = port->host_ops->host_init(port); ++ if (ret) ++ return ret; ++ } ++ ++ port->bridge = bridge; ++ plda_pcie_setup_window(port->bridge_addr, 0, cfg_res->start, 0, ++ resource_size(cfg_res)); ++ plda_pcie_setup_iomems(bridge, port); ++ plda_set_default_msi(&port->msi); ++ ret = plda_init_interrupts(pdev, port, plda_event); ++ if (ret) ++ goto err_host; ++ ++ /* Set default bus ops */ ++ bridge->ops = ops; ++ bridge->sysdata = port; ++ ++ ret = pci_host_probe(bridge); ++ if (ret < 0) { ++ dev_err_probe(dev, ret, "failed to probe pci host\n"); ++ goto err_probe; ++ } ++ ++ return ret; ++ ++err_probe: ++ plda_pcie_irq_domain_deinit(port); ++err_host: ++ if (port->host_ops && port->host_ops->host_deinit) ++ port->host_ops->host_deinit(port); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(plda_pcie_host_init); ++ ++void plda_pcie_host_deinit(struct plda_pcie_rp *port) ++{ ++ pci_stop_root_bus(port->bridge->bus); ++ pci_remove_root_bus(port->bridge->bus); ++ ++ plda_pcie_irq_domain_deinit(port); ++ ++ if (port->host_ops && port->host_ops->host_deinit) ++ port->host_ops->host_deinit(port); ++} ++EXPORT_SYMBOL_GPL(plda_pcie_host_deinit); +--- a/drivers/pci/controller/plda/pcie-plda.h ++++ b/drivers/pci/controller/plda/pcie-plda.h +@@ -141,6 +141,11 @@ struct plda_event_ops { + u32 (*get_events)(struct plda_pcie_rp *pcie); + }; + ++struct plda_pcie_host_ops { ++ int (*host_init)(struct plda_pcie_rp *pcie); ++ void (*host_deinit)(struct plda_pcie_rp *pcie); ++}; ++ + struct plda_msi { + struct mutex lock; /* Protect used bitmap */ + struct irq_domain *msi_domain; +@@ -152,14 +157,20 @@ struct plda_msi { + + struct plda_pcie_rp { + struct device *dev; ++ struct pci_host_bridge *bridge; + struct irq_domain *intx_domain; + struct irq_domain *event_domain; + raw_spinlock_t lock; + struct plda_msi msi; + const struct plda_event_ops *event_ops; + const struct irq_chip *event_irq_chip; ++ const struct plda_pcie_host_ops *host_ops; + void __iomem *bridge_addr; ++ void __iomem *config_base; + unsigned long events_bitmap; ++ int irq; ++ int msi_irq; ++ int intx_irq; + int num_events; + }; + +@@ -170,6 +181,8 @@ struct plda_event { + int msi_event; + }; + ++void __iomem *plda_pcie_map_bus(struct pci_bus *bus, unsigned int devfn, ++ int where); + int plda_init_interrupts(struct platform_device *pdev, + struct plda_pcie_rp *port, + const struct plda_event *event); +@@ -178,4 +191,13 @@ void plda_pcie_setup_window(void __iomem + size_t size); + int plda_pcie_setup_iomems(struct pci_host_bridge *bridge, + struct plda_pcie_rp *port); ++int plda_pcie_host_init(struct plda_pcie_rp *port, struct pci_ops *ops, ++ const struct plda_event *plda_event); ++void plda_pcie_host_deinit(struct plda_pcie_rp *pcie); ++ ++static inline void plda_set_default_msi(struct plda_msi *msi) ++{ ++ msi->vector_phy = IMSI_ADDR; ++ msi->num_vectors = PLDA_MAX_NUM_MSI_IRQS; ++} + #endif diff --git a/target/linux/starfive/patches-6.6/0032-dt-bindings-PCI-Add-StarFive-JH7110-PCIe-controller.patch b/target/linux/starfive/patches-6.6/0032-dt-bindings-PCI-Add-StarFive-JH7110-PCIe-controller.patch new file mode 100644 index 0000000000..9e33f7d68a --- /dev/null +++ b/target/linux/starfive/patches-6.6/0032-dt-bindings-PCI-Add-StarFive-JH7110-PCIe-controller.patch @@ -0,0 +1,140 @@ +From bc3f8207d9f0af3cb96a7eae232074a644a175f6 Mon Sep 17 00:00:00 2001 +From: Minda Chen +Date: Mon, 8 Jan 2024 19:06:09 +0800 +Subject: [PATCH 032/116] dt-bindings: PCI: Add StarFive JH7110 PCIe controller + +Add StarFive JH7110 SoC PCIe controller dt-bindings. JH7110 using PLDA +XpressRICH PCIe host controller IP. + +Signed-off-by: Minda Chen +Reviewed-by: Hal Feng +Reviewed-by: Conor Dooley +Reviewed-by: Rob Herring +--- + .../bindings/pci/starfive,jh7110-pcie.yaml | 120 ++++++++++++++++++ + 1 file changed, 120 insertions(+) + create mode 100644 Documentation/devicetree/bindings/pci/starfive,jh7110-pcie.yaml + +--- /dev/null ++++ b/Documentation/devicetree/bindings/pci/starfive,jh7110-pcie.yaml +@@ -0,0 +1,120 @@ ++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/pci/starfive,jh7110-pcie.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: StarFive JH7110 PCIe host controller ++ ++maintainers: ++ - Kevin Xie ++ ++allOf: ++ - $ref: plda,xpressrich3-axi-common.yaml# ++ ++properties: ++ compatible: ++ const: starfive,jh7110-pcie ++ ++ clocks: ++ items: ++ - description: NOC bus clock ++ - description: Transport layer clock ++ - description: AXI MST0 clock ++ - description: APB clock ++ ++ clock-names: ++ items: ++ - const: noc ++ - const: tl ++ - const: axi_mst0 ++ - const: apb ++ ++ resets: ++ items: ++ - description: AXI MST0 reset ++ - description: AXI SLAVE0 reset ++ - description: AXI SLAVE reset ++ - description: PCIE BRIDGE reset ++ - description: PCIE CORE reset ++ - description: PCIE APB reset ++ ++ reset-names: ++ items: ++ - const: mst0 ++ - const: slv0 ++ - const: slv ++ - const: brg ++ - const: core ++ - const: apb ++ ++ starfive,stg-syscon: ++ $ref: /schemas/types.yaml#/definitions/phandle-array ++ description: ++ The phandle to System Register Controller syscon node. ++ ++ perst-gpios: ++ description: GPIO controlled connection to PERST# signal ++ maxItems: 1 ++ ++ phys: ++ description: ++ Specified PHY is attached to PCIe controller. ++ maxItems: 1 ++ ++required: ++ - clocks ++ - resets ++ - starfive,stg-syscon ++ ++unevaluatedProperties: false ++ ++examples: ++ - | ++ #include ++ soc { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ++ pcie@940000000 { ++ compatible = "starfive,jh7110-pcie"; ++ reg = <0x9 0x40000000 0x0 0x10000000>, ++ <0x0 0x2b000000 0x0 0x1000000>; ++ reg-names = "cfg", "apb"; ++ #address-cells = <3>; ++ #size-cells = <2>; ++ #interrupt-cells = <1>; ++ device_type = "pci"; ++ ranges = <0x82000000 0x0 0x30000000 0x0 0x30000000 0x0 0x08000000>, ++ <0xc3000000 0x9 0x00000000 0x9 0x00000000 0x0 0x40000000>; ++ starfive,stg-syscon = <&stg_syscon>; ++ bus-range = <0x0 0xff>; ++ interrupt-parent = <&plic>; ++ interrupts = <56>; ++ interrupt-map-mask = <0x0 0x0 0x0 0x7>; ++ interrupt-map = <0x0 0x0 0x0 0x1 &pcie_intc0 0x1>, ++ <0x0 0x0 0x0 0x2 &pcie_intc0 0x2>, ++ <0x0 0x0 0x0 0x3 &pcie_intc0 0x3>, ++ <0x0 0x0 0x0 0x4 &pcie_intc0 0x4>; ++ msi-controller; ++ clocks = <&syscrg 86>, ++ <&stgcrg 10>, ++ <&stgcrg 8>, ++ <&stgcrg 9>; ++ clock-names = "noc", "tl", "axi_mst0", "apb"; ++ resets = <&stgcrg 11>, ++ <&stgcrg 12>, ++ <&stgcrg 13>, ++ <&stgcrg 14>, ++ <&stgcrg 15>, ++ <&stgcrg 16>; ++ perst-gpios = <&gpios 26 GPIO_ACTIVE_LOW>; ++ phys = <&pciephy0>; ++ ++ pcie_intc0: interrupt-controller { ++ #address-cells = <0>; ++ #interrupt-cells = <1>; ++ interrupt-controller; ++ }; ++ }; ++ }; diff --git a/target/linux/starfive/patches-6.6/0033-PCI-Add-PCIE_RESET_CONFIG_DEVICE_WAIT_MS-waiting-tim.patch b/target/linux/starfive/patches-6.6/0033-PCI-Add-PCIE_RESET_CONFIG_DEVICE_WAIT_MS-waiting-tim.patch new file mode 100644 index 0000000000..cc50dfe68e --- /dev/null +++ b/target/linux/starfive/patches-6.6/0033-PCI-Add-PCIE_RESET_CONFIG_DEVICE_WAIT_MS-waiting-tim.patch @@ -0,0 +1,55 @@ +From abb20b7b8f5e3a7f36dbd6264e6d346275434154 Mon Sep 17 00:00:00 2001 +From: Kevin Xie +Date: Mon, 8 Jan 2024 19:06:10 +0800 +Subject: [PATCH 033/116] PCI: Add PCIE_RESET_CONFIG_DEVICE_WAIT_MS waiting + time value + +Add the PCIE_RESET_CONFIG_DEVICE_WAIT_MS macro to define the minimum +waiting time between exit from a conventional reset and sending the +first configuration request to the device. + +As described in PCI base specification r6.0, section 6.6.1 , there are two different use cases of the value: + + - "With a Downstream Port that does not support Link speeds greater + than 5.0 GT/s, software must wait a minimum of 100 ms following exit + from a Conventional Reset before sending a Configuration Request to + the device immediately below that Port." + + - "With a Downstream Port that supports Link speeds greater than + 5.0 GT/s, software must wait a minimum of 100 ms after Link training + completes before sending a Configuration Request to the device + immediately below that Port." + +Signed-off-by: Kevin Xie +Reviewed-by: Mason Huo +Acked-by: Bjorn Helgaas +--- + drivers/pci/pci.h | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +--- a/drivers/pci/pci.h ++++ b/drivers/pci/pci.h +@@ -19,6 +19,22 @@ + */ + #define PCIE_PME_TO_L2_TIMEOUT_US 10000 + ++/* ++ * As described in PCI base specification r6.0, section 6.6.1 , there are two different use cases of the value: ++ * ++ * - "With a Downstream Port that does not support Link speeds greater ++ * than 5.0 GT/s, software must wait a minimum of 100 ms following exit ++ * from a Conventional Reset before sending a Configuration Request to ++ * the device immediately below that Port." ++ * ++ * - "With a Downstream Port that supports Link speeds greater than ++ * 5.0 GT/s, software must wait a minimum of 100 ms after Link training ++ * completes before sending a Configuration Request to the device ++ * immediately below that Port." ++ */ ++#define PCIE_RESET_CONFIG_DEVICE_WAIT_MS 100 ++ + extern const unsigned char pcie_link_speed[]; + extern bool pci_early_dump; + diff --git a/target/linux/starfive/patches-6.6/0034-PCI-starfive-Add-JH7110-PCIe-controller.patch b/target/linux/starfive/patches-6.6/0034-PCI-starfive-Add-JH7110-PCIe-controller.patch new file mode 100644 index 0000000000..45a549f489 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0034-PCI-starfive-Add-JH7110-PCIe-controller.patch @@ -0,0 +1,623 @@ +From 323aedef34315b758dc30ba23e2cabca259bb4b2 Mon Sep 17 00:00:00 2001 +From: Minda Chen +Date: Mon, 8 Jan 2024 19:06:11 +0800 +Subject: [PATCH 034/116] PCI: starfive: Add JH7110 PCIe controller + +Add StarFive JH7110 SoC PCIe controller platform driver codes, JH7110 +with PLDA host PCIe core. + +Signed-off-by: Minda Chen +Co-developed-by: Kevin Xie +Reviewed-by: Mason Huo +--- + drivers/pci/controller/plda/Kconfig | 12 + + drivers/pci/controller/plda/Makefile | 1 + + drivers/pci/controller/plda/pcie-plda.h | 71 ++- + drivers/pci/controller/plda/pcie-starfive.c | 473 ++++++++++++++++++++ + 4 files changed, 556 insertions(+), 1 deletion(-) + create mode 100644 drivers/pci/controller/plda/pcie-starfive.c + +--- a/drivers/pci/controller/plda/Kconfig ++++ b/drivers/pci/controller/plda/Kconfig +@@ -15,4 +15,16 @@ config PCIE_MICROCHIP_HOST + Say Y here if you want kernel to support the Microchip AXI PCIe + Host Bridge driver. + ++config PCIE_STARFIVE_HOST ++ tristate "StarFive PCIe host controller" ++ depends on PCI_MSI && OF ++ depends on ARCH_STARFIVE || COMPILE_TEST ++ select PCIE_PLDA_HOST ++ help ++ Say Y here if you want to support the StarFive PCIe controller in ++ host mode. StarFive PCIe controller uses PLDA PCIe core. ++ ++ If you choose to build this driver as module it will be dynamically ++ linked and module will be called pcie-starfive.ko. ++ + endmenu +--- a/drivers/pci/controller/plda/Makefile ++++ b/drivers/pci/controller/plda/Makefile +@@ -1,3 +1,4 @@ + # SPDX-License-Identifier: GPL-2.0 + obj-$(CONFIG_PCIE_PLDA_HOST) += pcie-plda-host.o + obj-$(CONFIG_PCIE_MICROCHIP_HOST) += pcie-microchip-host.o ++obj-$(CONFIG_PCIE_STARFIVE_HOST) += pcie-starfive.o +--- a/drivers/pci/controller/plda/pcie-plda.h ++++ b/drivers/pci/controller/plda/pcie-plda.h +@@ -10,10 +10,20 @@ + #define PLDA_MAX_NUM_MSI_IRQS 32 + + /* PCIe Bridge Phy Regs */ ++#define GEN_SETTINGS 0x80 ++#define RP_ENABLE 1 ++#define PCIE_PCI_IDS_DW1 0x9c ++#define IDS_CLASS_CODE_SHIFT 16 ++#define REVISION_ID_MASK GENMASK(7, 0) ++#define CLASS_CODE_ID_MASK GENMASK(31, 8) + #define PCIE_PCI_IRQ_DW0 0xa8 + #define MSIX_CAP_MASK BIT(31) + #define NUM_MSI_MSGS_MASK GENMASK(6, 4) + #define NUM_MSI_MSGS_SHIFT 4 ++#define PCI_MISC 0xb4 ++#define PHY_FUNCTION_DIS BIT(15) ++#define PCIE_WINROM 0xfc ++#define PREF_MEM_WIN_64_SUPPORT BIT(3) + + #define IMASK_LOCAL 0x180 + #define DMA_END_ENGINE_0_MASK 0x00000000u +@@ -65,6 +75,8 @@ + #define ISTATUS_HOST 0x18c + #define IMSI_ADDR 0x190 + #define ISTATUS_MSI 0x194 ++#define PMSG_SUPPORT_RX 0x3f0 ++#define PMSG_LTR_SUPPORT BIT(2) + + /* PCIe Master table init defines */ + #define ATR0_PCIE_WIN0_SRCADDR_PARAM 0x600u +@@ -86,6 +98,8 @@ + #define PCIE_TX_RX_INTERFACE 0x00000000u + #define PCIE_CONFIG_INTERFACE 0x00000001u + ++#define CONFIG_SPACE_ADDR_OFFSET 0x1000u ++ + #define ATR_ENTRY_SIZE 32 + + enum plda_int_event { +@@ -200,4 +214,59 @@ static inline void plda_set_default_msi( + msi->vector_phy = IMSI_ADDR; + msi->num_vectors = PLDA_MAX_NUM_MSI_IRQS; + } +-#endif ++ ++static inline void plda_pcie_enable_root_port(struct plda_pcie_rp *plda) ++{ ++ u32 value; ++ ++ value = readl_relaxed(plda->bridge_addr + GEN_SETTINGS); ++ value |= RP_ENABLE; ++ writel_relaxed(value, plda->bridge_addr + GEN_SETTINGS); ++} ++ ++static inline void plda_pcie_set_standard_class(struct plda_pcie_rp *plda) ++{ ++ u32 value; ++ ++ /* set class code and reserve revision id */ ++ value = readl_relaxed(plda->bridge_addr + PCIE_PCI_IDS_DW1); ++ value &= REVISION_ID_MASK; ++ value |= (PCI_CLASS_BRIDGE_PCI << IDS_CLASS_CODE_SHIFT); ++ writel_relaxed(value, plda->bridge_addr + PCIE_PCI_IDS_DW1); ++} ++ ++static inline void plda_pcie_set_pref_win_64bit(struct plda_pcie_rp *plda) ++{ ++ u32 value; ++ ++ value = readl_relaxed(plda->bridge_addr + PCIE_WINROM); ++ value |= PREF_MEM_WIN_64_SUPPORT; ++ writel_relaxed(value, plda->bridge_addr + PCIE_WINROM); ++} ++ ++static inline void plda_pcie_disable_ltr(struct plda_pcie_rp *plda) ++{ ++ u32 value; ++ ++ value = readl_relaxed(plda->bridge_addr + PMSG_SUPPORT_RX); ++ value &= ~PMSG_LTR_SUPPORT; ++ writel_relaxed(value, plda->bridge_addr + PMSG_SUPPORT_RX); ++} ++ ++static inline void plda_pcie_disable_func(struct plda_pcie_rp *plda) ++{ ++ u32 value; ++ ++ value = readl_relaxed(plda->bridge_addr + PCI_MISC); ++ value |= PHY_FUNCTION_DIS; ++ writel_relaxed(value, plda->bridge_addr + PCI_MISC); ++} ++ ++static inline void plda_pcie_write_rc_bar(struct plda_pcie_rp *plda, u64 val) ++{ ++ void __iomem *addr = plda->bridge_addr + CONFIG_SPACE_ADDR_OFFSET; ++ ++ writel_relaxed(lower_32_bits(val), addr + PCI_BASE_ADDRESS_0); ++ writel_relaxed(upper_32_bits(val), addr + PCI_BASE_ADDRESS_1); ++} ++#endif /* _PCIE_PLDA_H */ +--- /dev/null ++++ b/drivers/pci/controller/plda/pcie-starfive.c +@@ -0,0 +1,473 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * PCIe host controller driver for StarFive JH7110 Soc. ++ * ++ * Copyright (C) 2023 StarFive Technology Co., Ltd. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "../../pci.h" ++ ++#include "pcie-plda.h" ++ ++#define PCIE_FUNC_NUM 4 ++ ++/* system control */ ++#define STG_SYSCON_PCIE0_BASE 0x48 ++#define STG_SYSCON_PCIE1_BASE 0x1f8 ++ ++#define STG_SYSCON_AR_OFFSET 0x78 ++#define STG_SYSCON_AXI4_SLVL_AR_MASK GENMASK(22, 8) ++#define STG_SYSCON_AXI4_SLVL_PHY_AR(x) FIELD_PREP(GENMASK(20, 17), x) ++#define STG_SYSCON_AW_OFFSET 0x7c ++#define STG_SYSCON_AXI4_SLVL_AW_MASK GENMASK(14, 0) ++#define STG_SYSCON_AXI4_SLVL_PHY_AW(x) FIELD_PREP(GENMASK(12, 9), x) ++#define STG_SYSCON_CLKREQ BIT(22) ++#define STG_SYSCON_CKREF_SRC_MASK GENMASK(19, 18) ++#define STG_SYSCON_RP_NEP_OFFSET 0xe8 ++#define STG_SYSCON_K_RP_NEP BIT(8) ++#define STG_SYSCON_LNKSTA_OFFSET 0x170 ++#define DATA_LINK_ACTIVE BIT(5) ++ ++/* Parameters for the waiting for link up routine */ ++#define LINK_WAIT_MAX_RETRIES 10 ++#define LINK_WAIT_USLEEP_MIN 90000 ++#define LINK_WAIT_USLEEP_MAX 100000 ++ ++struct starfive_jh7110_pcie { ++ struct plda_pcie_rp plda; ++ struct reset_control *resets; ++ struct clk_bulk_data *clks; ++ struct regmap *reg_syscon; ++ struct gpio_desc *power_gpio; ++ struct gpio_desc *reset_gpio; ++ struct phy *phy; ++ ++ unsigned int stg_pcie_base; ++ int num_clks; ++}; ++ ++/* ++ * The BAR0/1 of bridge should be hidden during enumeration to ++ * avoid the sizing and resource allocation by PCIe core. ++ */ ++static bool starfive_pcie_hide_rc_bar(struct pci_bus *bus, unsigned int devfn, ++ int offset) ++{ ++ if (pci_is_root_bus(bus) && !devfn && ++ (offset == PCI_BASE_ADDRESS_0 || offset == PCI_BASE_ADDRESS_1)) ++ return true; ++ ++ return false; ++} ++ ++static int starfive_pcie_config_write(struct pci_bus *bus, unsigned int devfn, ++ int where, int size, u32 value) ++{ ++ if (starfive_pcie_hide_rc_bar(bus, devfn, where)) ++ return PCIBIOS_SUCCESSFUL; ++ ++ return pci_generic_config_write(bus, devfn, where, size, value); ++} ++ ++static int starfive_pcie_config_read(struct pci_bus *bus, unsigned int devfn, ++ int where, int size, u32 *value) ++{ ++ if (starfive_pcie_hide_rc_bar(bus, devfn, where)) { ++ *value = 0; ++ return PCIBIOS_SUCCESSFUL; ++ } ++ ++ return pci_generic_config_read(bus, devfn, where, size, value); ++} ++ ++static int starfive_pcie_parse_dt(struct starfive_jh7110_pcie *pcie, ++ struct device *dev) ++{ ++ int domain_nr; ++ ++ pcie->num_clks = devm_clk_bulk_get_all(dev, &pcie->clks); ++ if (pcie->num_clks < 0) ++ return dev_err_probe(dev, pcie->num_clks, ++ "failed to get pcie clocks\n"); ++ ++ pcie->resets = devm_reset_control_array_get_exclusive(dev); ++ if (IS_ERR(pcie->resets)) ++ return dev_err_probe(dev, PTR_ERR(pcie->resets), ++ "failed to get pcie resets"); ++ ++ pcie->reg_syscon = ++ syscon_regmap_lookup_by_phandle(dev->of_node, ++ "starfive,stg-syscon"); ++ ++ if (IS_ERR(pcie->reg_syscon)) ++ return dev_err_probe(dev, PTR_ERR(pcie->reg_syscon), ++ "failed to parse starfive,stg-syscon\n"); ++ ++ pcie->phy = devm_phy_optional_get(dev, NULL); ++ if (IS_ERR(pcie->phy)) ++ return dev_err_probe(dev, PTR_ERR(pcie->phy), ++ "failed to get pcie phy\n"); ++ ++ domain_nr = of_get_pci_domain_nr(dev->of_node); ++ ++ if (domain_nr < 0 || domain_nr > 1) ++ return dev_err_probe(dev, -ENODEV, ++ "failed to get valid pcie domain\n"); ++ ++ if (domain_nr == 0) ++ pcie->stg_pcie_base = STG_SYSCON_PCIE0_BASE; ++ else ++ pcie->stg_pcie_base = STG_SYSCON_PCIE1_BASE; ++ ++ pcie->reset_gpio = devm_gpiod_get_optional(dev, "perst", ++ GPIOD_OUT_HIGH); ++ if (IS_ERR(pcie->reset_gpio)) ++ return dev_err_probe(dev, PTR_ERR(pcie->reset_gpio), ++ "failed to get perst-gpio\n"); ++ ++ pcie->power_gpio = devm_gpiod_get_optional(dev, "enable", ++ GPIOD_OUT_LOW); ++ if (IS_ERR(pcie->power_gpio)) ++ return dev_err_probe(dev, PTR_ERR(pcie->power_gpio), ++ "failed to get power-gpio\n"); ++ ++ return 0; ++} ++ ++static struct pci_ops starfive_pcie_ops = { ++ .map_bus = plda_pcie_map_bus, ++ .read = starfive_pcie_config_read, ++ .write = starfive_pcie_config_write, ++}; ++ ++static int starfive_pcie_clk_rst_init(struct starfive_jh7110_pcie *pcie) ++{ ++ struct device *dev = pcie->plda.dev; ++ int ret; ++ ++ ret = clk_bulk_prepare_enable(pcie->num_clks, pcie->clks); ++ if (ret) ++ return dev_err_probe(dev, ret, "failed to enable clocks\n"); ++ ++ ret = reset_control_deassert(pcie->resets); ++ if (ret) { ++ clk_bulk_disable_unprepare(pcie->num_clks, pcie->clks); ++ dev_err_probe(dev, ret, "failed to deassert resets\n"); ++ } ++ ++ return ret; ++} ++ ++static void starfive_pcie_clk_rst_deinit(struct starfive_jh7110_pcie *pcie) ++{ ++ reset_control_assert(pcie->resets); ++ clk_bulk_disable_unprepare(pcie->num_clks, pcie->clks); ++} ++ ++static bool starfive_pcie_link_up(struct plda_pcie_rp *plda) ++{ ++ struct starfive_jh7110_pcie *pcie = ++ container_of(plda, struct starfive_jh7110_pcie, plda); ++ int ret; ++ u32 stg_reg_val; ++ ++ ret = regmap_read(pcie->reg_syscon, ++ pcie->stg_pcie_base + STG_SYSCON_LNKSTA_OFFSET, ++ &stg_reg_val); ++ if (ret) { ++ dev_err(pcie->plda.dev, "failed to read link status\n"); ++ return false; ++ } ++ ++ return !!(stg_reg_val & DATA_LINK_ACTIVE); ++} ++ ++static int starfive_pcie_host_wait_for_link(struct starfive_jh7110_pcie *pcie) ++{ ++ int retries; ++ ++ /* Check if the link is up or not */ ++ for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) { ++ if (starfive_pcie_link_up(&pcie->plda)) { ++ dev_info(pcie->plda.dev, "port link up\n"); ++ return 0; ++ } ++ usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX); ++ } ++ ++ return -ETIMEDOUT; ++} ++ ++static int starfive_pcie_enable_phy(struct device *dev, ++ struct starfive_jh7110_pcie *pcie) ++{ ++ int ret; ++ ++ if (!pcie->phy) ++ return 0; ++ ++ ret = phy_init(pcie->phy); ++ if (ret) ++ return dev_err_probe(dev, ret, ++ "failed to initialize pcie phy\n"); ++ ++ ret = phy_set_mode(pcie->phy, PHY_MODE_PCIE); ++ if (ret) { ++ dev_err_probe(dev, ret, "failed to set pcie mode\n"); ++ goto err_phy_on; ++ } ++ ++ ret = phy_power_on(pcie->phy); ++ if (ret) { ++ dev_err_probe(dev, ret, "failed to power on pcie phy\n"); ++ goto err_phy_on; ++ } ++ ++ return 0; ++ ++err_phy_on: ++ phy_exit(pcie->phy); ++ return ret; ++} ++ ++static void starfive_pcie_disable_phy(struct starfive_jh7110_pcie *pcie) ++{ ++ phy_power_off(pcie->phy); ++ phy_exit(pcie->phy); ++} ++ ++static void starfive_pcie_host_deinit(struct plda_pcie_rp *plda) ++{ ++ struct starfive_jh7110_pcie *pcie = ++ container_of(plda, struct starfive_jh7110_pcie, plda); ++ ++ starfive_pcie_clk_rst_deinit(pcie); ++ if (pcie->power_gpio) ++ gpiod_set_value_cansleep(pcie->power_gpio, 0); ++ starfive_pcie_disable_phy(pcie); ++} ++ ++static int starfive_pcie_host_init(struct plda_pcie_rp *plda) ++{ ++ struct starfive_jh7110_pcie *pcie = ++ container_of(plda, struct starfive_jh7110_pcie, plda); ++ struct device *dev = plda->dev; ++ int ret; ++ int i; ++ ++ ret = starfive_pcie_enable_phy(dev, pcie); ++ if (ret) ++ return ret; ++ ++ regmap_update_bits(pcie->reg_syscon, ++ pcie->stg_pcie_base + STG_SYSCON_RP_NEP_OFFSET, ++ STG_SYSCON_K_RP_NEP, STG_SYSCON_K_RP_NEP); ++ ++ regmap_update_bits(pcie->reg_syscon, ++ pcie->stg_pcie_base + STG_SYSCON_AW_OFFSET, ++ STG_SYSCON_CKREF_SRC_MASK, ++ FIELD_PREP(STG_SYSCON_CKREF_SRC_MASK, 2)); ++ ++ regmap_update_bits(pcie->reg_syscon, ++ pcie->stg_pcie_base + STG_SYSCON_AW_OFFSET, ++ STG_SYSCON_CLKREQ, STG_SYSCON_CLKREQ); ++ ++ ret = starfive_pcie_clk_rst_init(pcie); ++ if (ret) ++ return ret; ++ ++ if (pcie->power_gpio) ++ gpiod_set_value_cansleep(pcie->power_gpio, 1); ++ ++ if (pcie->reset_gpio) ++ gpiod_set_value_cansleep(pcie->reset_gpio, 1); ++ ++ /* Disable physical functions except #0 */ ++ for (i = 1; i < PCIE_FUNC_NUM; i++) { ++ regmap_update_bits(pcie->reg_syscon, ++ pcie->stg_pcie_base + STG_SYSCON_AR_OFFSET, ++ STG_SYSCON_AXI4_SLVL_AR_MASK, ++ STG_SYSCON_AXI4_SLVL_PHY_AR(i)); ++ ++ regmap_update_bits(pcie->reg_syscon, ++ pcie->stg_pcie_base + STG_SYSCON_AW_OFFSET, ++ STG_SYSCON_AXI4_SLVL_AW_MASK, ++ STG_SYSCON_AXI4_SLVL_PHY_AW(i)); ++ ++ plda_pcie_disable_func(plda); ++ } ++ ++ regmap_update_bits(pcie->reg_syscon, ++ pcie->stg_pcie_base + STG_SYSCON_AR_OFFSET, ++ STG_SYSCON_AXI4_SLVL_AR_MASK, 0); ++ regmap_update_bits(pcie->reg_syscon, ++ pcie->stg_pcie_base + STG_SYSCON_AW_OFFSET, ++ STG_SYSCON_AXI4_SLVL_AW_MASK, 0); ++ ++ plda_pcie_enable_root_port(plda); ++ plda_pcie_write_rc_bar(plda, 0); ++ ++ /* PCIe PCI Standard Configuration Identification Settings. */ ++ plda_pcie_set_standard_class(plda); ++ ++ /* ++ * The LTR message forwarding of PCIe Message Reception was set by core ++ * as default, but the forward id & addr are also need to be reset. ++ * If we do not disable LTR message forwarding here, or set a legal ++ * forwarding address, the kernel will get stuck after the driver probe. ++ * To workaround, disable the LTR message forwarding support on ++ * PCIe Message Reception. ++ */ ++ plda_pcie_disable_ltr(plda); ++ ++ /* Prefetchable memory window 64-bit addressing support */ ++ plda_pcie_set_pref_win_64bit(plda); ++ ++ /* ++ * Ensure that PERST has been asserted for at least 100 ms, ++ * the sleep value is T_PVPERL from PCIe CEM spec r2.0 (Table 2-4) ++ */ ++ msleep(100); ++ if (pcie->reset_gpio) ++ gpiod_set_value_cansleep(pcie->reset_gpio, 0); ++ ++ /* ++ * With a Downstream Port (<=5GT/s), software must wait a minimum ++ * of 100ms following exit from a conventional reset before ++ * sending a configuration request to the device. ++ */ ++ msleep(PCIE_RESET_CONFIG_DEVICE_WAIT_MS); ++ ++ if (starfive_pcie_host_wait_for_link(pcie)) ++ dev_info(dev, "port link down\n"); ++ ++ return 0; ++} ++ ++static const struct plda_pcie_host_ops sf_host_ops = { ++ .host_init = starfive_pcie_host_init, ++ .host_deinit = starfive_pcie_host_deinit, ++}; ++ ++static const struct plda_event stf_pcie_event = { ++ .intx_event = EVENT_PM_MSI_INT_INTX, ++ .msi_event = EVENT_PM_MSI_INT_MSI ++}; ++ ++static int starfive_pcie_probe(struct platform_device *pdev) ++{ ++ struct starfive_jh7110_pcie *pcie; ++ struct device *dev = &pdev->dev; ++ struct plda_pcie_rp *plda; ++ int ret; ++ ++ pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); ++ if (!pcie) ++ return -ENOMEM; ++ ++ plda = &pcie->plda; ++ plda->dev = dev; ++ ++ ret = starfive_pcie_parse_dt(pcie, dev); ++ if (ret) ++ return ret; ++ ++ plda->host_ops = &sf_host_ops; ++ plda->num_events = PLDA_MAX_EVENT_NUM; ++ /* mask doorbell event */ ++ plda->events_bitmap = GENMASK(PLDA_INT_EVENT_NUM - 1, 0) ++ & ~BIT(PLDA_AXI_DOORBELL) ++ & ~BIT(PLDA_PCIE_DOORBELL); ++ plda->events_bitmap <<= PLDA_NUM_DMA_EVENTS; ++ ret = plda_pcie_host_init(&pcie->plda, &starfive_pcie_ops, ++ &stf_pcie_event); ++ if (ret) ++ return ret; ++ ++ pm_runtime_enable(&pdev->dev); ++ pm_runtime_get_sync(&pdev->dev); ++ platform_set_drvdata(pdev, pcie); ++ ++ return 0; ++} ++ ++static void starfive_pcie_remove(struct platform_device *pdev) ++{ ++ struct starfive_jh7110_pcie *pcie = platform_get_drvdata(pdev); ++ ++ pm_runtime_put(&pdev->dev); ++ pm_runtime_disable(&pdev->dev); ++ plda_pcie_host_deinit(&pcie->plda); ++ platform_set_drvdata(pdev, NULL); ++} ++ ++static int starfive_pcie_suspend_noirq(struct device *dev) ++{ ++ struct starfive_jh7110_pcie *pcie = dev_get_drvdata(dev); ++ ++ clk_bulk_disable_unprepare(pcie->num_clks, pcie->clks); ++ starfive_pcie_disable_phy(pcie); ++ ++ return 0; ++} ++ ++static int starfive_pcie_resume_noirq(struct device *dev) ++{ ++ struct starfive_jh7110_pcie *pcie = dev_get_drvdata(dev); ++ int ret; ++ ++ ret = starfive_pcie_enable_phy(dev, pcie); ++ if (ret) ++ return ret; ++ ++ ret = clk_bulk_prepare_enable(pcie->num_clks, pcie->clks); ++ if (ret) { ++ dev_err(dev, "failed to enable clocks\n"); ++ starfive_pcie_disable_phy(pcie); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static const struct dev_pm_ops starfive_pcie_pm_ops = { ++ NOIRQ_SYSTEM_SLEEP_PM_OPS(starfive_pcie_suspend_noirq, ++ starfive_pcie_resume_noirq) ++}; ++ ++static const struct of_device_id starfive_pcie_of_match[] = { ++ { .compatible = "starfive,jh7110-pcie", }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, starfive_pcie_of_match); ++ ++static struct platform_driver starfive_pcie_driver = { ++ .driver = { ++ .name = "pcie-starfive", ++ .of_match_table = of_match_ptr(starfive_pcie_of_match), ++ .pm = pm_sleep_ptr(&starfive_pcie_pm_ops), ++ }, ++ .probe = starfive_pcie_probe, ++ .remove_new = starfive_pcie_remove, ++}; ++module_platform_driver(starfive_pcie_driver); ++ ++MODULE_DESCRIPTION("StarFive JH7110 PCIe host driver"); ++MODULE_LICENSE("GPL v2"); diff --git a/target/linux/starfive/patches-6.6/0035-ASoC-dt-bindings-Add-StarFive-JH7110-PWM-DAC-control.patch b/target/linux/starfive/patches-6.6/0035-ASoC-dt-bindings-Add-StarFive-JH7110-PWM-DAC-control.patch new file mode 100644 index 0000000000..cf14242f4a --- /dev/null +++ b/target/linux/starfive/patches-6.6/0035-ASoC-dt-bindings-Add-StarFive-JH7110-PWM-DAC-control.patch @@ -0,0 +1,97 @@ +From a306724fd4f32808d1e27efbd87019d56f60db20 Mon Sep 17 00:00:00 2001 +From: Hal Feng +Date: Mon, 14 Aug 2023 16:06:16 +0800 +Subject: [PATCH 035/116] ASoC: dt-bindings: Add StarFive JH7110 PWM-DAC + controller + +Add bindings for the PWM-DAC controller on the JH7110 +RISC-V SoC by StarFive Ltd. + +Reviewed-by: Krzysztof Kozlowski +Signed-off-by: Hal Feng +Link: https://lore.kernel.org/r/20230814080618.10036-2-hal.feng@starfivetech.com +Signed-off-by: Mark Brown +--- + .../sound/starfive,jh7110-pwmdac.yaml | 76 +++++++++++++++++++ + 1 file changed, 76 insertions(+) + create mode 100644 Documentation/devicetree/bindings/sound/starfive,jh7110-pwmdac.yaml + +--- /dev/null ++++ b/Documentation/devicetree/bindings/sound/starfive,jh7110-pwmdac.yaml +@@ -0,0 +1,76 @@ ++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/sound/starfive,jh7110-pwmdac.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: StarFive JH7110 PWM-DAC Controller ++ ++description: ++ The PWM-DAC Controller uses PWM square wave generators plus RC filters to ++ form a DAC for audio play in StarFive JH7110 SoC. This audio play controller ++ supports 16 bit audio format, up to 48K sampling frequency, up to left and ++ right dual channels. ++ ++maintainers: ++ - Hal Feng ++ ++allOf: ++ - $ref: dai-common.yaml# ++ ++properties: ++ compatible: ++ const: starfive,jh7110-pwmdac ++ ++ reg: ++ maxItems: 1 ++ ++ clocks: ++ items: ++ - description: PWMDAC APB ++ - description: PWMDAC CORE ++ ++ clock-names: ++ items: ++ - const: apb ++ - const: core ++ ++ resets: ++ maxItems: 1 ++ description: PWMDAC APB ++ ++ dmas: ++ maxItems: 1 ++ description: TX DMA Channel ++ ++ dma-names: ++ const: tx ++ ++ "#sound-dai-cells": ++ const: 0 ++ ++required: ++ - compatible ++ - reg ++ - clocks ++ - clock-names ++ - resets ++ - dmas ++ - dma-names ++ - "#sound-dai-cells" ++ ++additionalProperties: false ++ ++examples: ++ - | ++ pwmdac@100b0000 { ++ compatible = "starfive,jh7110-pwmdac"; ++ reg = <0x100b0000 0x1000>; ++ clocks = <&syscrg 157>, ++ <&syscrg 158>; ++ clock-names = "apb", "core"; ++ resets = <&syscrg 96>; ++ dmas = <&dma 22>; ++ dma-names = "tx"; ++ #sound-dai-cells = <0>; ++ }; diff --git a/target/linux/starfive/patches-6.6/0036-ASoC-starfive-Add-JH7110-PWM-DAC-driver.patch b/target/linux/starfive/patches-6.6/0036-ASoC-starfive-Add-JH7110-PWM-DAC-driver.patch new file mode 100644 index 0000000000..acc859317b --- /dev/null +++ b/target/linux/starfive/patches-6.6/0036-ASoC-starfive-Add-JH7110-PWM-DAC-driver.patch @@ -0,0 +1,574 @@ +From a79d2ec524012e35e32a2c4ae2401d0aa763697d Mon Sep 17 00:00:00 2001 +From: Hal Feng +Date: Mon, 14 Aug 2023 16:06:17 +0800 +Subject: [PATCH 036/116] ASoC: starfive: Add JH7110 PWM-DAC driver + +Add PWM-DAC driver support for the StarFive JH7110 SoC. + +Reviewed-by: Walker Chen +Signed-off-by: Hal Feng +Link: https://lore.kernel.org/r/20230814080618.10036-3-hal.feng@starfivetech.com +Signed-off-by: Mark Brown +--- + sound/soc/starfive/Kconfig | 9 + + sound/soc/starfive/Makefile | 1 + + sound/soc/starfive/jh7110_pwmdac.c | 529 +++++++++++++++++++++++++++++ + 3 files changed, 539 insertions(+) + create mode 100644 sound/soc/starfive/jh7110_pwmdac.c + +--- a/sound/soc/starfive/Kconfig ++++ b/sound/soc/starfive/Kconfig +@@ -7,6 +7,15 @@ config SND_SOC_STARFIVE + the Starfive SoCs' Audio interfaces. You will also need to + select the audio interfaces to support below. + ++config SND_SOC_JH7110_PWMDAC ++ tristate "JH7110 PWM-DAC device driver" ++ depends on HAVE_CLK && SND_SOC_STARFIVE ++ select SND_SOC_GENERIC_DMAENGINE_PCM ++ select SND_SOC_SPDIF ++ help ++ Say Y or M if you want to add support for StarFive JH7110 ++ PWM-DAC driver. ++ + config SND_SOC_JH7110_TDM + tristate "JH7110 TDM device driver" + depends on HAVE_CLK && SND_SOC_STARFIVE +--- a/sound/soc/starfive/Makefile ++++ b/sound/soc/starfive/Makefile +@@ -1,2 +1,3 @@ + # StarFive Platform Support ++obj-$(CONFIG_SND_SOC_JH7110_PWMDAC) += jh7110_pwmdac.o + obj-$(CONFIG_SND_SOC_JH7110_TDM) += jh7110_tdm.o +--- /dev/null ++++ b/sound/soc/starfive/jh7110_pwmdac.c +@@ -0,0 +1,529 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * jh7110_pwmdac.c -- StarFive JH7110 PWM-DAC driver ++ * ++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd. ++ * ++ * Authors: Jenny Zhang ++ * Curry Zhang ++ * Xingyu Wu ++ * Hal Feng ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define JH7110_PWMDAC_WDATA 0x00 ++#define JH7110_PWMDAC_CTRL 0x04 ++ #define JH7110_PWMDAC_ENABLE BIT(0) ++ #define JH7110_PWMDAC_SHIFT BIT(1) ++ #define JH7110_PWMDAC_DUTY_CYCLE_SHIFT 2 ++ #define JH7110_PWMDAC_DUTY_CYCLE_MASK GENMASK(3, 2) ++ #define JH7110_PWMDAC_CNT_N_SHIFT 4 ++ #define JH7110_PWMDAC_CNT_N_MASK GENMASK(12, 4) ++ #define JH7110_PWMDAC_DATA_CHANGE BIT(13) ++ #define JH7110_PWMDAC_DATA_MODE BIT(14) ++ #define JH7110_PWMDAC_DATA_SHIFT_SHIFT 15 ++ #define JH7110_PWMDAC_DATA_SHIFT_MASK GENMASK(17, 15) ++ ++enum JH7110_PWMDAC_SHIFT_VAL { ++ PWMDAC_SHIFT_8 = 0, ++ PWMDAC_SHIFT_10, ++}; ++ ++enum JH7110_PWMDAC_DUTY_CYCLE_VAL { ++ PWMDAC_CYCLE_LEFT = 0, ++ PWMDAC_CYCLE_RIGHT, ++ PWMDAC_CYCLE_CENTER, ++}; ++ ++enum JH7110_PWMDAC_CNT_N_VAL { ++ PWMDAC_SAMPLE_CNT_1 = 1, ++ PWMDAC_SAMPLE_CNT_2, ++ PWMDAC_SAMPLE_CNT_3, ++ PWMDAC_SAMPLE_CNT_512 = 512, /* max */ ++}; ++ ++enum JH7110_PWMDAC_DATA_CHANGE_VAL { ++ NO_CHANGE = 0, ++ CHANGE, ++}; ++ ++enum JH7110_PWMDAC_DATA_MODE_VAL { ++ UNSIGNED_DATA = 0, ++ INVERTER_DATA_MSB, ++}; ++ ++enum JH7110_PWMDAC_DATA_SHIFT_VAL { ++ 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, ++}; ++ ++struct jh7110_pwmdac_cfg { ++ enum JH7110_PWMDAC_SHIFT_VAL shift; ++ enum JH7110_PWMDAC_DUTY_CYCLE_VAL duty_cycle; ++ u16 cnt_n; ++ enum JH7110_PWMDAC_DATA_CHANGE_VAL data_change; ++ enum JH7110_PWMDAC_DATA_MODE_VAL data_mode; ++ enum JH7110_PWMDAC_DATA_SHIFT_VAL data_shift; ++}; ++ ++struct jh7110_pwmdac_dev { ++ void __iomem *base; ++ resource_size_t mapbase; ++ struct jh7110_pwmdac_cfg cfg; ++ ++ struct clk_bulk_data clks[2]; ++ struct reset_control *rst_apb; ++ struct device *dev; ++ struct snd_dmaengine_dai_dma_data play_dma_data; ++ u32 saved_ctrl; ++}; ++ ++static inline void jh7110_pwmdac_write_reg(void __iomem *io_base, int reg, u32 val) ++{ ++ writel(val, io_base + reg); ++} ++ ++static inline u32 jh7110_pwmdac_read_reg(void __iomem *io_base, int reg) ++{ ++ return readl(io_base + reg); ++} ++ ++static void jh7110_pwmdac_set_enable(struct jh7110_pwmdac_dev *dev, bool enable) ++{ ++ u32 value; ++ ++ value = jh7110_pwmdac_read_reg(dev->base, JH7110_PWMDAC_CTRL); ++ if (enable) ++ value |= JH7110_PWMDAC_ENABLE; ++ else ++ value &= ~JH7110_PWMDAC_ENABLE; ++ ++ jh7110_pwmdac_write_reg(dev->base, JH7110_PWMDAC_CTRL, value); ++} ++ ++static void jh7110_pwmdac_set_shift(struct jh7110_pwmdac_dev *dev) ++{ ++ u32 value; ++ ++ value = jh7110_pwmdac_read_reg(dev->base, JH7110_PWMDAC_CTRL); ++ if (dev->cfg.shift == PWMDAC_SHIFT_8) ++ value &= ~JH7110_PWMDAC_SHIFT; ++ else if (dev->cfg.shift == PWMDAC_SHIFT_10) ++ value |= JH7110_PWMDAC_SHIFT; ++ ++ jh7110_pwmdac_write_reg(dev->base, JH7110_PWMDAC_CTRL, value); ++} ++ ++static void jh7110_pwmdac_set_duty_cycle(struct jh7110_pwmdac_dev *dev) ++{ ++ u32 value; ++ ++ value = jh7110_pwmdac_read_reg(dev->base, JH7110_PWMDAC_CTRL); ++ value &= ~JH7110_PWMDAC_DUTY_CYCLE_MASK; ++ value |= (dev->cfg.duty_cycle & 0x3) << JH7110_PWMDAC_DUTY_CYCLE_SHIFT; ++ ++ jh7110_pwmdac_write_reg(dev->base, JH7110_PWMDAC_CTRL, value); ++} ++ ++static void jh7110_pwmdac_set_cnt_n(struct jh7110_pwmdac_dev *dev) ++{ ++ u32 value; ++ ++ value = jh7110_pwmdac_read_reg(dev->base, JH7110_PWMDAC_CTRL); ++ value &= ~JH7110_PWMDAC_CNT_N_MASK; ++ value |= ((dev->cfg.cnt_n - 1) & 0x1ff) << JH7110_PWMDAC_CNT_N_SHIFT; ++ ++ jh7110_pwmdac_write_reg(dev->base, JH7110_PWMDAC_CTRL, value); ++} ++ ++static void jh7110_pwmdac_set_data_change(struct jh7110_pwmdac_dev *dev) ++{ ++ u32 value; ++ ++ value = jh7110_pwmdac_read_reg(dev->base, JH7110_PWMDAC_CTRL); ++ if (dev->cfg.data_change == NO_CHANGE) ++ value &= ~JH7110_PWMDAC_DATA_CHANGE; ++ else if (dev->cfg.data_change == CHANGE) ++ value |= JH7110_PWMDAC_DATA_CHANGE; ++ ++ jh7110_pwmdac_write_reg(dev->base, JH7110_PWMDAC_CTRL, value); ++} ++ ++static void jh7110_pwmdac_set_data_mode(struct jh7110_pwmdac_dev *dev) ++{ ++ u32 value; ++ ++ value = jh7110_pwmdac_read_reg(dev->base, JH7110_PWMDAC_CTRL); ++ if (dev->cfg.data_mode == UNSIGNED_DATA) ++ value &= ~JH7110_PWMDAC_DATA_MODE; ++ else if (dev->cfg.data_mode == INVERTER_DATA_MSB) ++ value |= JH7110_PWMDAC_DATA_MODE; ++ ++ jh7110_pwmdac_write_reg(dev->base, JH7110_PWMDAC_CTRL, value); ++} ++ ++static void jh7110_pwmdac_set_data_shift(struct jh7110_pwmdac_dev *dev) ++{ ++ u32 value; ++ ++ value = jh7110_pwmdac_read_reg(dev->base, JH7110_PWMDAC_CTRL); ++ value &= ~JH7110_PWMDAC_DATA_SHIFT_MASK; ++ value |= (dev->cfg.data_shift & 0x7) << JH7110_PWMDAC_DATA_SHIFT_SHIFT; ++ ++ jh7110_pwmdac_write_reg(dev->base, JH7110_PWMDAC_CTRL, value); ++} ++ ++static void jh7110_pwmdac_set(struct jh7110_pwmdac_dev *dev) ++{ ++ jh7110_pwmdac_set_shift(dev); ++ jh7110_pwmdac_set_duty_cycle(dev); ++ jh7110_pwmdac_set_cnt_n(dev); ++ jh7110_pwmdac_set_enable(dev, true); ++ ++ jh7110_pwmdac_set_data_change(dev); ++ jh7110_pwmdac_set_data_mode(dev); ++ jh7110_pwmdac_set_data_shift(dev); ++} ++ ++static void jh7110_pwmdac_stop(struct jh7110_pwmdac_dev *dev) ++{ ++ jh7110_pwmdac_set_enable(dev, false); ++} ++ ++static int jh7110_pwmdac_startup(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); ++ struct snd_soc_dai_link *dai_link = rtd->dai_link; ++ ++ dai_link->trigger_stop = SND_SOC_TRIGGER_ORDER_LDC; ++ ++ return 0; ++} ++ ++static int jh7110_pwmdac_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params, ++ struct snd_soc_dai *dai) ++{ ++ struct jh7110_pwmdac_dev *dev = dev_get_drvdata(dai->dev); ++ unsigned long core_clk_rate; ++ int ret; ++ ++ switch (params_rate(params)) { ++ case 8000: ++ dev->cfg.cnt_n = PWMDAC_SAMPLE_CNT_3; ++ core_clk_rate = 6144000; ++ break; ++ case 11025: ++ dev->cfg.cnt_n = PWMDAC_SAMPLE_CNT_2; ++ core_clk_rate = 5644800; ++ break; ++ case 16000: ++ dev->cfg.cnt_n = PWMDAC_SAMPLE_CNT_3; ++ core_clk_rate = 12288000; ++ break; ++ case 22050: ++ dev->cfg.cnt_n = PWMDAC_SAMPLE_CNT_1; ++ core_clk_rate = 5644800; ++ break; ++ case 32000: ++ dev->cfg.cnt_n = PWMDAC_SAMPLE_CNT_1; ++ core_clk_rate = 8192000; ++ break; ++ case 44100: ++ dev->cfg.cnt_n = PWMDAC_SAMPLE_CNT_1; ++ core_clk_rate = 11289600; ++ break; ++ case 48000: ++ dev->cfg.cnt_n = PWMDAC_SAMPLE_CNT_1; ++ core_clk_rate = 12288000; ++ break; ++ default: ++ dev_err(dai->dev, "%d rate not supported\n", ++ params_rate(params)); ++ return -EINVAL; ++ } ++ ++ switch (params_channels(params)) { ++ case 1: ++ dev->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; ++ break; ++ case 2: ++ dev->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; ++ break; ++ default: ++ dev_err(dai->dev, "%d channels not supported\n", ++ params_channels(params)); ++ return -EINVAL; ++ } ++ ++ /* ++ * The clock rate always rounds down when using clk_set_rate() ++ * so increase the rate a bit ++ */ ++ core_clk_rate += 64; ++ jh7110_pwmdac_set(dev); ++ ++ ret = clk_set_rate(dev->clks[1].clk, core_clk_rate); ++ if (ret) ++ return dev_err_probe(dai->dev, ret, ++ "failed to set rate %lu for core clock\n", ++ core_clk_rate); ++ ++ return 0; ++} ++ ++static int jh7110_pwmdac_trigger(struct snd_pcm_substream *substream, int cmd, ++ struct snd_soc_dai *dai) ++{ ++ struct jh7110_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: ++ jh7110_pwmdac_set(dev); ++ break; ++ ++ case SNDRV_PCM_TRIGGER_STOP: ++ case SNDRV_PCM_TRIGGER_SUSPEND: ++ case SNDRV_PCM_TRIGGER_PAUSE_PUSH: ++ jh7110_pwmdac_stop(dev); ++ break; ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ ++ return ret; ++} ++ ++static int jh7110_pwmdac_crg_enable(struct jh7110_pwmdac_dev *dev, bool enable) ++{ ++ int ret; ++ ++ if (enable) { ++ ret = clk_bulk_prepare_enable(ARRAY_SIZE(dev->clks), dev->clks); ++ if (ret) ++ return dev_err_probe(dev->dev, ret, ++ "failed to enable pwmdac clocks\n"); ++ ++ ret = reset_control_deassert(dev->rst_apb); ++ if (ret) { ++ dev_err(dev->dev, "failed to deassert pwmdac apb reset\n"); ++ goto err_rst_apb; ++ } ++ } else { ++ clk_bulk_disable_unprepare(ARRAY_SIZE(dev->clks), dev->clks); ++ } ++ ++ return 0; ++ ++err_rst_apb: ++ clk_bulk_disable_unprepare(ARRAY_SIZE(dev->clks), dev->clks); ++ ++ return ret; ++} ++ ++static int jh7110_pwmdac_dai_probe(struct snd_soc_dai *dai) ++{ ++ struct jh7110_pwmdac_dev *dev = dev_get_drvdata(dai->dev); ++ ++ snd_soc_dai_init_dma_data(dai, &dev->play_dma_data, NULL); ++ snd_soc_dai_set_drvdata(dai, dev); ++ ++ return 0; ++} ++ ++static const struct snd_soc_dai_ops jh7110_pwmdac_dai_ops = { ++ .startup = jh7110_pwmdac_startup, ++ .hw_params = jh7110_pwmdac_hw_params, ++ .trigger = jh7110_pwmdac_trigger, ++}; ++ ++static const struct snd_soc_component_driver jh7110_pwmdac_component = { ++ .name = "jh7110-pwmdac", ++}; ++ ++static struct snd_soc_dai_driver jh7110_pwmdac_dai = { ++ .name = "jh7110-pwmdac", ++ .id = 0, ++ .probe = jh7110_pwmdac_dai_probe, ++ .playback = { ++ .channels_min = 1, ++ .channels_max = 2, ++ .rates = SNDRV_PCM_RATE_8000_48000, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE, ++ }, ++ .ops = &jh7110_pwmdac_dai_ops, ++}; ++ ++static int jh7110_pwmdac_runtime_suspend(struct device *dev) ++{ ++ struct jh7110_pwmdac_dev *pwmdac = dev_get_drvdata(dev); ++ ++ return jh7110_pwmdac_crg_enable(pwmdac, false); ++} ++ ++static int jh7110_pwmdac_runtime_resume(struct device *dev) ++{ ++ struct jh7110_pwmdac_dev *pwmdac = dev_get_drvdata(dev); ++ ++ return jh7110_pwmdac_crg_enable(pwmdac, true); ++} ++ ++static int jh7110_pwmdac_system_suspend(struct device *dev) ++{ ++ struct jh7110_pwmdac_dev *pwmdac = dev_get_drvdata(dev); ++ ++ /* save the CTRL register value */ ++ pwmdac->saved_ctrl = jh7110_pwmdac_read_reg(pwmdac->base, ++ JH7110_PWMDAC_CTRL); ++ return pm_runtime_force_suspend(dev); ++} ++ ++static int jh7110_pwmdac_system_resume(struct device *dev) ++{ ++ struct jh7110_pwmdac_dev *pwmdac = dev_get_drvdata(dev); ++ int ret; ++ ++ ret = pm_runtime_force_resume(dev); ++ if (ret) ++ return ret; ++ ++ /* restore the CTRL register value */ ++ jh7110_pwmdac_write_reg(pwmdac->base, JH7110_PWMDAC_CTRL, ++ pwmdac->saved_ctrl); ++ return 0; ++} ++ ++static const struct dev_pm_ops jh7110_pwmdac_pm_ops = { ++ RUNTIME_PM_OPS(jh7110_pwmdac_runtime_suspend, ++ jh7110_pwmdac_runtime_resume, NULL) ++ SYSTEM_SLEEP_PM_OPS(jh7110_pwmdac_system_suspend, ++ jh7110_pwmdac_system_resume) ++}; ++ ++static void jh7110_pwmdac_init_params(struct jh7110_pwmdac_dev *dev) ++{ ++ dev->cfg.shift = PWMDAC_SHIFT_8; ++ dev->cfg.duty_cycle = PWMDAC_CYCLE_CENTER; ++ dev->cfg.cnt_n = PWMDAC_SAMPLE_CNT_1; ++ dev->cfg.data_change = NO_CHANGE; ++ dev->cfg.data_mode = INVERTER_DATA_MSB; ++ dev->cfg.data_shift = PWMDAC_DATA_LEFT_SHIFT_BIT_0; ++ ++ dev->play_dma_data.addr = dev->mapbase + JH7110_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; ++} ++ ++static int jh7110_pwmdac_probe(struct platform_device *pdev) ++{ ++ struct jh7110_pwmdac_dev *dev; ++ struct resource *res; ++ int ret; ++ ++ dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); ++ if (!dev) ++ return -ENOMEM; ++ ++ dev->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); ++ if (IS_ERR(dev->base)) ++ return PTR_ERR(dev->base); ++ ++ dev->mapbase = res->start; ++ ++ dev->clks[0].id = "apb"; ++ dev->clks[1].id = "core"; ++ ++ ret = devm_clk_bulk_get(&pdev->dev, ARRAY_SIZE(dev->clks), dev->clks); ++ if (ret) ++ return dev_err_probe(&pdev->dev, ret, ++ "failed to get pwmdac clocks\n"); ++ ++ dev->rst_apb = devm_reset_control_get_exclusive(&pdev->dev, NULL); ++ if (IS_ERR(dev->rst_apb)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(dev->rst_apb), ++ "failed to get pwmdac apb reset\n"); ++ ++ jh7110_pwmdac_init_params(dev); ++ ++ dev->dev = &pdev->dev; ++ dev_set_drvdata(&pdev->dev, dev); ++ ret = devm_snd_soc_register_component(&pdev->dev, ++ &jh7110_pwmdac_component, ++ &jh7110_pwmdac_dai, 1); ++ if (ret) ++ return dev_err_probe(&pdev->dev, ret, "failed to register dai\n"); ++ ++ ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); ++ if (ret) ++ return dev_err_probe(&pdev->dev, ret, "failed to register pcm\n"); ++ ++ pm_runtime_enable(dev->dev); ++ if (!pm_runtime_enabled(&pdev->dev)) { ++ ret = jh7110_pwmdac_runtime_resume(&pdev->dev); ++ if (ret) ++ goto err_pm_disable; ++ } ++ ++ return 0; ++ ++err_pm_disable: ++ pm_runtime_disable(&pdev->dev); ++ ++ return ret; ++} ++ ++static int jh7110_pwmdac_remove(struct platform_device *pdev) ++{ ++ pm_runtime_disable(&pdev->dev); ++ return 0; ++} ++ ++static const struct of_device_id jh7110_pwmdac_of_match[] = { ++ { .compatible = "starfive,jh7110-pwmdac" }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, jh7110_pwmdac_of_match); ++ ++static struct platform_driver jh7110_pwmdac_driver = { ++ .driver = { ++ .name = "jh7110-pwmdac", ++ .of_match_table = jh7110_pwmdac_of_match, ++ .pm = pm_ptr(&jh7110_pwmdac_pm_ops), ++ }, ++ .probe = jh7110_pwmdac_probe, ++ .remove = jh7110_pwmdac_remove, ++}; ++module_platform_driver(jh7110_pwmdac_driver); ++ ++MODULE_AUTHOR("Jenny Zhang"); ++MODULE_AUTHOR("Curry Zhang"); ++MODULE_AUTHOR("Xingyu Wu "); ++MODULE_AUTHOR("Hal Feng "); ++MODULE_DESCRIPTION("StarFive JH7110 PWM-DAC driver"); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/starfive/patches-6.6/0037-ASoC-Update-jh7110-PWM-DAC-for-ops-move.patch b/target/linux/starfive/patches-6.6/0037-ASoC-Update-jh7110-PWM-DAC-for-ops-move.patch new file mode 100644 index 0000000000..4833067bac --- /dev/null +++ b/target/linux/starfive/patches-6.6/0037-ASoC-Update-jh7110-PWM-DAC-for-ops-move.patch @@ -0,0 +1,32 @@ +From c6b693f990e1f89ab5af0a139da31401b8cda74f Mon Sep 17 00:00:00 2001 +From: Mark Brown +Date: Mon, 11 Sep 2023 23:48:39 +0100 +Subject: [PATCH 037/116] ASoC: Update jh7110 PWM DAC for ops move + +For some reason the JH7110 PWM DAC driver made it through build testing +in spite of not being updated for the move of probe() to the ops struct. +Make the required update. + +Signed-off-by: Mark Brown +--- + sound/soc/starfive/jh7110_pwmdac.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/sound/soc/starfive/jh7110_pwmdac.c ++++ b/sound/soc/starfive/jh7110_pwmdac.c +@@ -357,6 +357,7 @@ static int jh7110_pwmdac_dai_probe(struc + } + + static const struct snd_soc_dai_ops jh7110_pwmdac_dai_ops = { ++ .probe = jh7110_pwmdac_dai_probe, + .startup = jh7110_pwmdac_startup, + .hw_params = jh7110_pwmdac_hw_params, + .trigger = jh7110_pwmdac_trigger, +@@ -369,7 +370,6 @@ static const struct snd_soc_component_dr + static struct snd_soc_dai_driver jh7110_pwmdac_dai = { + .name = "jh7110-pwmdac", + .id = 0, +- .probe = jh7110_pwmdac_dai_probe, + .playback = { + .channels_min = 1, + .channels_max = 2, diff --git a/target/linux/starfive/patches-6.6/0038-ASoC-starfive-jh7110-pwmdac-Convert-to-platform-remo.patch b/target/linux/starfive/patches-6.6/0038-ASoC-starfive-jh7110-pwmdac-Convert-to-platform-remo.patch new file mode 100644 index 0000000000..9b5f553f8c --- /dev/null +++ b/target/linux/starfive/patches-6.6/0038-ASoC-starfive-jh7110-pwmdac-Convert-to-platform-remo.patch @@ -0,0 +1,52 @@ +From 312c3c407c363f0ec7417d2d389cbe936c503729 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= +Date: Sat, 14 Oct 2023 00:19:49 +0200 +Subject: [PATCH 038/116] ASoC: starfive/jh7110-pwmdac: Convert to platform + remove callback returning void +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The .remove() callback for a platform driver returns an int which makes +many driver authors wrongly assume it's possible to do error handling by +returning an error code. However the value returned is ignored (apart +from emitting a warning) and this typically results in resource leaks. + +To improve here there is a quest to make the remove callback return +void. In the first step of this quest all drivers are converted to +.remove_new(), which already returns void. Eventually after all drivers +are converted, .remove_new() will be renamed to .remove(). + +Trivially convert this driver from always returning zero in the remove +callback to the void returning variant. + +Signed-off-by: Uwe Kleine-König +Link: https://lore.kernel.org/r/20231013221945.1489203-12-u.kleine-koenig@pengutronix.de +Signed-off-by: Mark Brown +--- + sound/soc/starfive/jh7110_pwmdac.c | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +--- a/sound/soc/starfive/jh7110_pwmdac.c ++++ b/sound/soc/starfive/jh7110_pwmdac.c +@@ -498,10 +498,9 @@ err_pm_disable: + return ret; + } + +-static int jh7110_pwmdac_remove(struct platform_device *pdev) ++static void jh7110_pwmdac_remove(struct platform_device *pdev) + { + pm_runtime_disable(&pdev->dev); +- return 0; + } + + static const struct of_device_id jh7110_pwmdac_of_match[] = { +@@ -517,7 +516,7 @@ static struct platform_driver jh7110_pwm + .pm = pm_ptr(&jh7110_pwmdac_pm_ops), + }, + .probe = jh7110_pwmdac_probe, +- .remove = jh7110_pwmdac_remove, ++ .remove_new = jh7110_pwmdac_remove, + }; + module_platform_driver(jh7110_pwmdac_driver); + diff --git a/target/linux/starfive/patches-6.6/0039-dt-bindings-power-Add-power-domain-header-for-JH7110.patch b/target/linux/starfive/patches-6.6/0039-dt-bindings-power-Add-power-domain-header-for-JH7110.patch new file mode 100644 index 0000000000..bcc9af2d2c --- /dev/null +++ b/target/linux/starfive/patches-6.6/0039-dt-bindings-power-Add-power-domain-header-for-JH7110.patch @@ -0,0 +1,35 @@ +From 8d84bac6d7471ba2e29b33d19a2ef887822e9cce Mon Sep 17 00:00:00 2001 +From: Changhuang Liang +Date: Wed, 13 Sep 2023 14:54:25 +0100 +Subject: [PATCH 039/116] dt-bindings: power: Add power-domain header for + JH7110 + +Add power-domain header for JH7110 SoC, it can use to operate dphy +power. + +Signed-off-by: Changhuang Liang +Signed-off-by: Conor Dooley +Link: https://lore.kernel.org/r/20230913-grumbly-rewrite-34c85539f2ed@spud +Signed-off-by: Ulf Hansson +--- + include/dt-bindings/power/starfive,jh7110-pmu.h | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +--- a/include/dt-bindings/power/starfive,jh7110-pmu.h ++++ b/include/dt-bindings/power/starfive,jh7110-pmu.h +@@ -1,6 +1,6 @@ + /* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ + /* +- * Copyright (C) 2022 StarFive Technology Co., Ltd. ++ * Copyright (C) 2022-2023 StarFive Technology Co., Ltd. + * Author: Walker Chen + */ + #ifndef __DT_BINDINGS_POWER_JH7110_POWER_H__ +@@ -14,4 +14,7 @@ + #define JH7110_PD_ISP 5 + #define JH7110_PD_VENC 6 + ++#define JH7110_PD_DPHY_TX 0 ++#define JH7110_PD_DPHY_RX 1 ++ + #endif diff --git a/target/linux/starfive/patches-6.6/0040-pmdomain-starfive-Replace-SOC_STARFIVE-with-ARCH_STA.patch b/target/linux/starfive/patches-6.6/0040-pmdomain-starfive-Replace-SOC_STARFIVE-with-ARCH_STA.patch new file mode 100644 index 0000000000..335193fbb6 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0040-pmdomain-starfive-Replace-SOC_STARFIVE-with-ARCH_STA.patch @@ -0,0 +1,31 @@ +From 0ac8e8b0e65d242f455401df0cc6c6d4772216e6 Mon Sep 17 00:00:00 2001 +From: Changhuang Liang +Date: Wed, 13 Sep 2023 14:54:26 +0100 +Subject: [PATCH 040/116] pmdomain: starfive: Replace SOC_STARFIVE with + ARCH_STARFIVE + +Using ARCH_FOO symbol is preferred than SOC_FOO. + +Reviewed-by: Conor Dooley +Reviewed-by: Walker Chen +Signed-off-by: Changhuang Liang +Signed-off-by: Conor Dooley +Link: https://lore.kernel.org/r/20230913-legibly-treachery-567cffcb5604@spud +Signed-off-by: Ulf Hansson +--- + drivers/soc/starfive/Kconfig | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/soc/starfive/Kconfig ++++ b/drivers/soc/starfive/Kconfig +@@ -3,8 +3,8 @@ + config JH71XX_PMU + bool "Support PMU for StarFive JH71XX Soc" + depends on PM +- depends on SOC_STARFIVE || COMPILE_TEST +- default SOC_STARFIVE ++ depends on ARCH_STARFIVE || COMPILE_TEST ++ default ARCH_STARFIVE + select PM_GENERIC_DOMAINS + help + Say 'y' here to enable support power domain support. diff --git a/target/linux/starfive/patches-6.6/0041-pmdomain-starfive-Extract-JH7110-pmu-private-operati.patch b/target/linux/starfive/patches-6.6/0041-pmdomain-starfive-Extract-JH7110-pmu-private-operati.patch new file mode 100644 index 0000000000..7425dd6dbb --- /dev/null +++ b/target/linux/starfive/patches-6.6/0041-pmdomain-starfive-Extract-JH7110-pmu-private-operati.patch @@ -0,0 +1,179 @@ +From a1ba60e35ca7f1b85243054556ecde2e259619e1 Mon Sep 17 00:00:00 2001 +From: Changhuang Liang +Date: Wed, 13 Sep 2023 14:54:27 +0100 +Subject: [PATCH 041/116] pmdomain: starfive: Extract JH7110 pmu private + operations + +Move JH7110 private operation into private data of compatible. Convenient +to add AON PMU which would not have interrupts property. + +Signed-off-by: Changhuang Liang +Reviewed-by: Walker Chen +Signed-off-by: Conor Dooley +Link: https://lore.kernel.org/r/20230913-slideshow-luckiness-38ff17de84c6@spud +Signed-off-by: Ulf Hansson +--- + drivers/pmdomain/starfive/jh71xx-pmu.c | 89 ++++++++++++++++++-------- + 1 file changed, 62 insertions(+), 27 deletions(-) + +--- a/drivers/pmdomain/starfive/jh71xx-pmu.c ++++ b/drivers/pmdomain/starfive/jh71xx-pmu.c +@@ -51,9 +51,17 @@ struct jh71xx_domain_info { + u8 bit; + }; + ++struct jh71xx_pmu; ++struct jh71xx_pmu_dev; ++ + struct jh71xx_pmu_match_data { + const struct jh71xx_domain_info *domain_info; + int num_domains; ++ unsigned int pmu_status; ++ int (*pmu_parse_irq)(struct platform_device *pdev, ++ struct jh71xx_pmu *pmu); ++ int (*pmu_set_state)(struct jh71xx_pmu_dev *pmd, ++ u32 mask, bool on); + }; + + struct jh71xx_pmu { +@@ -79,12 +87,12 @@ static int jh71xx_pmu_get_state(struct j + if (!mask) + return -EINVAL; + +- *is_on = readl(pmu->base + JH71XX_PMU_CURR_POWER_MODE) & mask; ++ *is_on = readl(pmu->base + pmu->match_data->pmu_status) & mask; + + return 0; + } + +-static int jh71xx_pmu_set_state(struct jh71xx_pmu_dev *pmd, u32 mask, bool on) ++static int jh7110_pmu_set_state(struct jh71xx_pmu_dev *pmd, u32 mask, bool on) + { + struct jh71xx_pmu *pmu = pmd->pmu; + unsigned long flags; +@@ -92,22 +100,8 @@ static int jh71xx_pmu_set_state(struct j + u32 mode; + u32 encourage_lo; + u32 encourage_hi; +- bool is_on; + int ret; + +- ret = jh71xx_pmu_get_state(pmd, mask, &is_on); +- if (ret) { +- dev_dbg(pmu->dev, "unable to get current state for %s\n", +- pmd->genpd.name); +- return ret; +- } +- +- if (is_on == on) { +- dev_dbg(pmu->dev, "pm domain [%s] is already %sable status.\n", +- pmd->genpd.name, on ? "en" : "dis"); +- return 0; +- } +- + spin_lock_irqsave(&pmu->lock, flags); + + /* +@@ -166,6 +160,29 @@ static int jh71xx_pmu_set_state(struct j + return 0; + } + ++static int jh71xx_pmu_set_state(struct jh71xx_pmu_dev *pmd, u32 mask, bool on) ++{ ++ struct jh71xx_pmu *pmu = pmd->pmu; ++ const struct jh71xx_pmu_match_data *match_data = pmu->match_data; ++ bool is_on; ++ int ret; ++ ++ ret = jh71xx_pmu_get_state(pmd, mask, &is_on); ++ if (ret) { ++ dev_dbg(pmu->dev, "unable to get current state for %s\n", ++ pmd->genpd.name); ++ return ret; ++ } ++ ++ if (is_on == on) { ++ dev_dbg(pmu->dev, "pm domain [%s] is already %sable status.\n", ++ pmd->genpd.name, on ? "en" : "dis"); ++ return 0; ++ } ++ ++ return match_data->pmu_set_state(pmd, mask, on); ++} ++ + static int jh71xx_pmu_on(struct generic_pm_domain *genpd) + { + struct jh71xx_pmu_dev *pmd = container_of(genpd, +@@ -226,6 +243,25 @@ static irqreturn_t jh71xx_pmu_interrupt( + return IRQ_HANDLED; + } + ++static int jh7110_pmu_parse_irq(struct platform_device *pdev, struct jh71xx_pmu *pmu) ++{ ++ struct device *dev = &pdev->dev; ++ int ret; ++ ++ pmu->irq = platform_get_irq(pdev, 0); ++ if (pmu->irq < 0) ++ return pmu->irq; ++ ++ ret = devm_request_irq(dev, pmu->irq, jh71xx_pmu_interrupt, ++ 0, pdev->name, pmu); ++ if (ret) ++ dev_err(dev, "failed to request irq\n"); ++ ++ jh71xx_pmu_int_enable(pmu, JH71XX_PMU_INT_ALL_MASK & ~JH71XX_PMU_INT_PCH_FAIL, true); ++ ++ return 0; ++} ++ + static int jh71xx_pmu_init_domain(struct jh71xx_pmu *pmu, int index) + { + struct jh71xx_pmu_dev *pmd; +@@ -275,19 +311,18 @@ static int jh71xx_pmu_probe(struct platf + if (IS_ERR(pmu->base)) + return PTR_ERR(pmu->base); + +- pmu->irq = platform_get_irq(pdev, 0); +- if (pmu->irq < 0) +- return pmu->irq; +- +- ret = devm_request_irq(dev, pmu->irq, jh71xx_pmu_interrupt, +- 0, pdev->name, pmu); +- if (ret) +- dev_err(dev, "failed to request irq\n"); ++ spin_lock_init(&pmu->lock); + + match_data = of_device_get_match_data(dev); + if (!match_data) + return -EINVAL; + ++ ret = match_data->pmu_parse_irq(pdev, pmu); ++ if (ret) { ++ dev_err(dev, "failed to parse irq\n"); ++ return ret; ++ } ++ + pmu->genpd = devm_kcalloc(dev, match_data->num_domains, + sizeof(struct generic_pm_domain *), + GFP_KERNEL); +@@ -307,9 +342,6 @@ static int jh71xx_pmu_probe(struct platf + } + } + +- spin_lock_init(&pmu->lock); +- jh71xx_pmu_int_enable(pmu, JH71XX_PMU_INT_ALL_MASK & ~JH71XX_PMU_INT_PCH_FAIL, true); +- + ret = of_genpd_add_provider_onecell(np, &pmu->genpd_data); + if (ret) { + dev_err(dev, "failed to register genpd driver: %d\n", ret); +@@ -357,6 +389,9 @@ static const struct jh71xx_domain_info j + static const struct jh71xx_pmu_match_data jh7110_pmu = { + .num_domains = ARRAY_SIZE(jh7110_power_domains), + .domain_info = jh7110_power_domains, ++ .pmu_status = JH71XX_PMU_CURR_POWER_MODE, ++ .pmu_parse_irq = jh7110_pmu_parse_irq, ++ .pmu_set_state = jh7110_pmu_set_state, + }; + + static const struct of_device_id jh71xx_pmu_of_match[] = { diff --git a/target/linux/starfive/patches-6.6/0042-pmdomain-starfive-Add-JH7110-AON-PMU-support.patch b/target/linux/starfive/patches-6.6/0042-pmdomain-starfive-Add-JH7110-AON-PMU-support.patch new file mode 100644 index 0000000000..2563d5421c --- /dev/null +++ b/target/linux/starfive/patches-6.6/0042-pmdomain-starfive-Add-JH7110-AON-PMU-support.patch @@ -0,0 +1,122 @@ +From 1bf849b606d0b4cae643f96685d3d3981643683d Mon Sep 17 00:00:00 2001 +From: Changhuang Liang +Date: Wed, 13 Sep 2023 14:54:28 +0100 +Subject: [PATCH 042/116] pmdomain: starfive: Add JH7110 AON PMU support + +Add AON PMU for StarFive JH7110 SoC. It can be used to turn on/off the +dphy rx/tx power switch. + +Reviewed-by: Walker Chen +Signed-off-by: Changhuang Liang +Signed-off-by: Conor Dooley +Link: https://lore.kernel.org/r/20230913-dude-imprecise-fc32622bc947@spud +Signed-off-by: Ulf Hansson +--- + drivers/pmdomain/starfive/jh71xx-pmu.c | 57 +++++++++++++++++++++++--- + 1 file changed, 52 insertions(+), 5 deletions(-) + +--- a/drivers/pmdomain/starfive/jh71xx-pmu.c ++++ b/drivers/pmdomain/starfive/jh71xx-pmu.c +@@ -2,7 +2,7 @@ + /* + * StarFive JH71XX PMU (Power Management Unit) Controller Driver + * +- * Copyright (C) 2022 StarFive Technology Co., Ltd. ++ * Copyright (C) 2022-2023 StarFive Technology Co., Ltd. + */ + + #include +@@ -24,6 +24,9 @@ + #define JH71XX_PMU_EVENT_STATUS 0x88 + #define JH71XX_PMU_INT_STATUS 0x8C + ++/* aon pmu register offset */ ++#define JH71XX_AON_PMU_SWITCH 0x00 ++ + /* sw encourage cfg */ + #define JH71XX_PMU_SW_ENCOURAGE_EN_LO 0x05 + #define JH71XX_PMU_SW_ENCOURAGE_EN_HI 0x50 +@@ -160,6 +163,26 @@ static int jh7110_pmu_set_state(struct j + return 0; + } + ++static int jh7110_aon_pmu_set_state(struct jh71xx_pmu_dev *pmd, u32 mask, bool on) ++{ ++ struct jh71xx_pmu *pmu = pmd->pmu; ++ unsigned long flags; ++ u32 val; ++ ++ spin_lock_irqsave(&pmu->lock, flags); ++ val = readl(pmu->base + JH71XX_AON_PMU_SWITCH); ++ ++ if (on) ++ val |= mask; ++ else ++ val &= ~mask; ++ ++ writel(val, pmu->base + JH71XX_AON_PMU_SWITCH); ++ spin_unlock_irqrestore(&pmu->lock, flags); ++ ++ return 0; ++} ++ + static int jh71xx_pmu_set_state(struct jh71xx_pmu_dev *pmd, u32 mask, bool on) + { + struct jh71xx_pmu *pmu = pmd->pmu; +@@ -317,10 +340,12 @@ static int jh71xx_pmu_probe(struct platf + if (!match_data) + return -EINVAL; + +- ret = match_data->pmu_parse_irq(pdev, pmu); +- if (ret) { +- dev_err(dev, "failed to parse irq\n"); +- return ret; ++ if (match_data->pmu_parse_irq) { ++ ret = match_data->pmu_parse_irq(pdev, pmu); ++ if (ret) { ++ dev_err(dev, "failed to parse irq\n"); ++ return ret; ++ } + } + + pmu->genpd = devm_kcalloc(dev, match_data->num_domains, +@@ -394,11 +419,32 @@ static const struct jh71xx_pmu_match_dat + .pmu_set_state = jh7110_pmu_set_state, + }; + ++static const struct jh71xx_domain_info jh7110_aon_power_domains[] = { ++ [JH7110_PD_DPHY_TX] = { ++ .name = "DPHY-TX", ++ .bit = 30, ++ }, ++ [JH7110_PD_DPHY_RX] = { ++ .name = "DPHY-RX", ++ .bit = 31, ++ }, ++}; ++ ++static const struct jh71xx_pmu_match_data jh7110_aon_pmu = { ++ .num_domains = ARRAY_SIZE(jh7110_aon_power_domains), ++ .domain_info = jh7110_aon_power_domains, ++ .pmu_status = JH71XX_AON_PMU_SWITCH, ++ .pmu_set_state = jh7110_aon_pmu_set_state, ++}; ++ + static const struct of_device_id jh71xx_pmu_of_match[] = { + { + .compatible = "starfive,jh7110-pmu", + .data = (void *)&jh7110_pmu, + }, { ++ .compatible = "starfive,jh7110-aon-syscon", ++ .data = (void *)&jh7110_aon_pmu, ++ }, { + /* sentinel */ + } + }; +@@ -414,5 +460,6 @@ static struct platform_driver jh71xx_pmu + builtin_platform_driver(jh71xx_pmu_driver); + + MODULE_AUTHOR("Walker Chen "); ++MODULE_AUTHOR("Changhuang Liang "); + MODULE_DESCRIPTION("StarFive JH71XX PMU Driver"); + MODULE_LICENSE("GPL"); diff --git a/target/linux/starfive/patches-6.6/0043-pmdomain-Prepare-to-move-Kconfig-files-into-the-pmdo.patch b/target/linux/starfive/patches-6.6/0043-pmdomain-Prepare-to-move-Kconfig-files-into-the-pmdo.patch new file mode 100644 index 0000000000..52be89f29e --- /dev/null +++ b/target/linux/starfive/patches-6.6/0043-pmdomain-Prepare-to-move-Kconfig-files-into-the-pmdo.patch @@ -0,0 +1,36 @@ +From dff6605dcd1fc1e2af1437e59187a6f71ce389cd Mon Sep 17 00:00:00 2001 +From: Ulf Hansson +Date: Mon, 11 Sep 2023 17:22:15 +0200 +Subject: [PATCH 043/116] pmdomain: Prepare to move Kconfig files into the + pmdomain subsystem + +Rather than having the various Kconfig files for the genpd providers +sprinkled across subsystems, let's prepare to move them into the pmdomain +subsystem along with the implementations. + +Reviewed-by: Geert Uytterhoeven +Signed-off-by: Ulf Hansson +--- + drivers/Kconfig | 2 ++ + drivers/pmdomain/Kconfig | 4 ++++ + 2 files changed, 6 insertions(+) + create mode 100644 drivers/pmdomain/Kconfig + +--- a/drivers/Kconfig ++++ b/drivers/Kconfig +@@ -175,6 +175,8 @@ source "drivers/soundwire/Kconfig" + + source "drivers/soc/Kconfig" + ++source "drivers/pmdomain/Kconfig" ++ + source "drivers/devfreq/Kconfig" + + source "drivers/extcon/Kconfig" +--- /dev/null ++++ b/drivers/pmdomain/Kconfig +@@ -0,0 +1,4 @@ ++# SPDX-License-Identifier: GPL-2.0-only ++menu "PM Domains" ++ ++endmenu diff --git a/target/linux/starfive/patches-6.6/0044-pmdomain-starfive-Move-Kconfig-file-to-the-pmdomain-.patch b/target/linux/starfive/patches-6.6/0044-pmdomain-starfive-Move-Kconfig-file-to-the-pmdomain-.patch new file mode 100644 index 0000000000..909f4cc63a --- /dev/null +++ b/target/linux/starfive/patches-6.6/0044-pmdomain-starfive-Move-Kconfig-file-to-the-pmdomain-.patch @@ -0,0 +1,69 @@ +From de12fe43dbd0ea9fa980ffa05822bd7fd5eed330 Mon Sep 17 00:00:00 2001 +From: Ulf Hansson +Date: Tue, 12 Sep 2023 13:31:44 +0200 +Subject: [PATCH 044/116] pmdomain: starfive: Move Kconfig file to the pmdomain + subsystem + +The Kconfig belongs closer to the corresponding implementation, hence let's +move it from the soc subsystem to the pmdomain subsystem. + +Cc: Walker Chen +Cc: Conor Dooley +Acked-by: Conor Dooley +Signed-off-by: Ulf Hansson +--- + drivers/pmdomain/Kconfig | 2 ++ + drivers/{soc => pmdomain}/starfive/Kconfig | 0 + drivers/soc/Kconfig | 1 - + 3 files changed, 2 insertions(+), 1 deletion(-) + rename drivers/{soc => pmdomain}/starfive/Kconfig (100%) + +--- a/drivers/pmdomain/Kconfig ++++ b/drivers/pmdomain/Kconfig +@@ -1,4 +1,6 @@ + # SPDX-License-Identifier: GPL-2.0-only + menu "PM Domains" + ++source "drivers/pmdomain/starfive/Kconfig" ++ + endmenu +--- a/drivers/soc/Kconfig ++++ b/drivers/soc/Kconfig +@@ -24,7 +24,6 @@ source "drivers/soc/renesas/Kconfig" + source "drivers/soc/rockchip/Kconfig" + source "drivers/soc/samsung/Kconfig" + source "drivers/soc/sifive/Kconfig" +-source "drivers/soc/starfive/Kconfig" + source "drivers/soc/sunxi/Kconfig" + source "drivers/soc/tegra/Kconfig" + source "drivers/soc/ti/Kconfig" +--- /dev/null ++++ b/drivers/pmdomain/starfive/Kconfig +@@ -0,0 +1,12 @@ ++# SPDX-License-Identifier: GPL-2.0 ++ ++config JH71XX_PMU ++ bool "Support PMU for StarFive JH71XX Soc" ++ depends on PM ++ depends on ARCH_STARFIVE || COMPILE_TEST ++ default ARCH_STARFIVE ++ select PM_GENERIC_DOMAINS ++ help ++ Say 'y' here to enable support power domain support. ++ In order to meet low power requirements, a Power Management Unit (PMU) ++ is designed for controlling power resources in StarFive JH71XX SoCs. +--- a/drivers/soc/starfive/Kconfig ++++ /dev/null +@@ -1,12 +0,0 @@ +-# SPDX-License-Identifier: GPL-2.0 +- +-config JH71XX_PMU +- bool "Support PMU for StarFive JH71XX Soc" +- depends on PM +- depends on ARCH_STARFIVE || COMPILE_TEST +- default ARCH_STARFIVE +- select PM_GENERIC_DOMAINS +- help +- Say 'y' here to enable support power domain support. +- In order to meet low power requirements, a Power Management Unit (PMU) +- is designed for controlling power resources in StarFive JH71XX SoCs. diff --git a/target/linux/starfive/patches-6.6/0045-dt-bindings-power-Update-prefixes-for-AON-power-doma.patch b/target/linux/starfive/patches-6.6/0045-dt-bindings-power-Update-prefixes-for-AON-power-doma.patch new file mode 100644 index 0000000000..c6e0012528 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0045-dt-bindings-power-Update-prefixes-for-AON-power-doma.patch @@ -0,0 +1,31 @@ +From cac9ce9c7f388a741389b1ec47af65420254db55 Mon Sep 17 00:00:00 2001 +From: Changhuang Liang +Date: Wed, 27 Sep 2023 06:07:33 -0700 +Subject: [PATCH 045/116] dt-bindings: power: Update prefixes for AON power + domain + +Use "JH7110_AON_PD_" prefix for AON power domain for JH7110 SoC. + +Reviewed-by: Walker Chen +Signed-off-by: Changhuang Liang +Acked-by: Conor Dooley +Reviewed-by: Geert Uytterhoeven +Link: https://lore.kernel.org/r/20230927130734.9921-2-changhuang.liang@starfivetech.com +Signed-off-by: Ulf Hansson +--- + include/dt-bindings/power/starfive,jh7110-pmu.h | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +--- a/include/dt-bindings/power/starfive,jh7110-pmu.h ++++ b/include/dt-bindings/power/starfive,jh7110-pmu.h +@@ -14,7 +14,8 @@ + #define JH7110_PD_ISP 5 + #define JH7110_PD_VENC 6 + +-#define JH7110_PD_DPHY_TX 0 +-#define JH7110_PD_DPHY_RX 1 ++/* AON Power Domain */ ++#define JH7110_AON_PD_DPHY_TX 0 ++#define JH7110_AON_PD_DPHY_RX 1 + + #endif diff --git a/target/linux/starfive/patches-6.6/0046-pmdomain-starfive-Update-prefixes-for-AON-power-doma.patch b/target/linux/starfive/patches-6.6/0046-pmdomain-starfive-Update-prefixes-for-AON-power-doma.patch new file mode 100644 index 0000000000..5cb89ae052 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0046-pmdomain-starfive-Update-prefixes-for-AON-power-doma.patch @@ -0,0 +1,32 @@ +From 3ea89ffbd6cc5a15acca6bc2130572f8bd85b9d4 Mon Sep 17 00:00:00 2001 +From: Changhuang Liang +Date: Wed, 27 Sep 2023 06:07:34 -0700 +Subject: [PATCH 046/116] pmdomain: starfive: Update prefixes for AON power + domain + +Use "JH7110_AON_PD_" prefix for AON power doamin for JH7110 SoC. + +Reviewed-by: Walker Chen +Signed-off-by: Changhuang Liang +Link: https://lore.kernel.org/r/20230927130734.9921-3-changhuang.liang@starfivetech.com +Signed-off-by: Ulf Hansson +--- + drivers/pmdomain/starfive/jh71xx-pmu.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/pmdomain/starfive/jh71xx-pmu.c ++++ b/drivers/pmdomain/starfive/jh71xx-pmu.c +@@ -420,11 +420,11 @@ static const struct jh71xx_pmu_match_dat + }; + + static const struct jh71xx_domain_info jh7110_aon_power_domains[] = { +- [JH7110_PD_DPHY_TX] = { ++ [JH7110_AON_PD_DPHY_TX] = { + .name = "DPHY-TX", + .bit = 30, + }, +- [JH7110_PD_DPHY_RX] = { ++ [JH7110_AON_PD_DPHY_RX] = { + .name = "DPHY-RX", + .bit = 31, + }, diff --git a/target/linux/starfive/patches-6.6/0047-riscv-dts-starfive-pinfunc-Fix-the-pins-name-of-I2ST.patch b/target/linux/starfive/patches-6.6/0047-riscv-dts-starfive-pinfunc-Fix-the-pins-name-of-I2ST.patch new file mode 100644 index 0000000000..782e71a0eb --- /dev/null +++ b/target/linux/starfive/patches-6.6/0047-riscv-dts-starfive-pinfunc-Fix-the-pins-name-of-I2ST.patch @@ -0,0 +1,30 @@ +From e7e3d62b7a470ddf15e30574232b52b2e23ba606 Mon Sep 17 00:00:00 2001 +From: Xingyu Wu +Date: Mon, 21 Aug 2023 22:41:50 +0800 +Subject: [PATCH 047/116] riscv: dts: starfive: pinfunc: Fix the pins name of + I2STX1 + +These pins are actually I2STX1 clock input, not I2STX0, +so their names should be changed. + +Signed-off-by: Xingyu Wu +Reviewed-by: Walker Chen +Acked-by: Rob Herring +Signed-off-by: Conor Dooley +--- + arch/riscv/boot/dts/starfive/jh7110-pinfunc.h | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/arch/riscv/boot/dts/starfive/jh7110-pinfunc.h ++++ b/arch/riscv/boot/dts/starfive/jh7110-pinfunc.h +@@ -240,8 +240,8 @@ + #define GPI_SYS_MCLK_EXT 30 + #define GPI_SYS_I2SRX_BCLK 31 + #define GPI_SYS_I2SRX_LRCK 32 +-#define GPI_SYS_I2STX0_BCLK 33 +-#define GPI_SYS_I2STX0_LRCK 34 ++#define GPI_SYS_I2STX1_BCLK 33 ++#define GPI_SYS_I2STX1_LRCK 34 + #define GPI_SYS_TDM_CLK 35 + #define GPI_SYS_TDM_RXD 36 + #define GPI_SYS_TDM_SYNC 37 diff --git a/target/linux/starfive/patches-6.6/0048-riscv-dts-starfive-Add-full-support-except-VIN-and-V.patch b/target/linux/starfive/patches-6.6/0048-riscv-dts-starfive-Add-full-support-except-VIN-and-V.patch new file mode 100644 index 0000000000..ad6626cd79 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0048-riscv-dts-starfive-Add-full-support-except-VIN-and-V.patch @@ -0,0 +1,549 @@ +From a3d3f611f31fa2dca3deefa7cd443abca02e03fa Mon Sep 17 00:00:00 2001 +From: Hal Feng +Date: Tue, 11 Apr 2023 16:31:15 +0800 +Subject: [PATCH 048/116] riscv: dts: starfive: Add full support (except VIN + and VOUT) for JH7110 and VisionFive 2 board + +Merge all StarFive dts patches together except VIN and VOUT. + +Signed-off-by: Hal Feng +--- + .../jh7110-starfive-visionfive-2.dtsi | 199 +++++++++++++++ + arch/riscv/boot/dts/starfive/jh7110.dtsi | 233 ++++++++++++++++++ + 2 files changed, 432 insertions(+) + +--- a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi ++++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi +@@ -19,6 +19,8 @@ + i2c6 = &i2c6; + mmc0 = &mmc0; + mmc1 = &mmc1; ++ pcie0 = &pcie0; ++ pcie1 = &pcie1; + serial0 = &uart0; + }; + +@@ -40,6 +42,33 @@ + gpios = <&sysgpio 35 GPIO_ACTIVE_HIGH>; + priority = <224>; + }; ++ ++ pwmdac_codec: pwmdac-codec { ++ compatible = "linux,spdif-dit"; ++ #sound-dai-cells = <0>; ++ }; ++ ++ sound-pwmdac { ++ compatible = "simple-audio-card"; ++ simple-audio-card,name = "StarFive-PWMDAC-Sound-Card"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ simple-audio-card,dai-link@0 { ++ reg = <0>; ++ format = "left_j"; ++ bitclock-master = <&sndcpu0>; ++ frame-master = <&sndcpu0>; ++ ++ sndcpu0: cpu { ++ sound-dai = <&pwmdac>; ++ }; ++ ++ codec { ++ sound-dai = <&pwmdac_codec>; ++ }; ++ }; ++ }; + }; + + &dvp_clk { +@@ -202,8 +231,28 @@ + status = "okay"; + }; + ++&i2srx { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2srx_pins>; ++ status = "okay"; ++}; ++ ++&i2stx0 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&mclk_ext_pins>; ++ status = "okay"; ++}; ++ ++&i2stx1 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2stx1_pins>; ++ status = "okay"; ++}; ++ + &mmc0 { + max-frequency = <100000000>; ++ assigned-clocks = <&syscrg JH7110_SYSCLK_SDIO0_SDCARD>; ++ assigned-clock-rates = <50000000>; + bus-width = <8>; + cap-mmc-highspeed; + mmc-ddr-1_8v; +@@ -220,6 +269,8 @@ + + &mmc1 { + max-frequency = <100000000>; ++ assigned-clocks = <&syscrg JH7110_SYSCLK_SDIO1_SDCARD>; ++ assigned-clock-rates = <50000000>; + bus-width = <4>; + no-sdio; + no-mmc; +@@ -231,6 +282,34 @@ + status = "okay"; + }; + ++&pcie0 { ++ perst-gpios = <&sysgpio 26 GPIO_ACTIVE_LOW>; ++ phys = <&pciephy0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pcie0_pins>; ++ status = "okay"; ++}; ++ ++&pcie1 { ++ perst-gpios = <&sysgpio 28 GPIO_ACTIVE_LOW>; ++ phys = <&pciephy1>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pcie1_pins>; ++ status = "okay"; ++}; ++ ++&pwm { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pwm_pins>; ++ status = "okay"; ++}; ++ ++&pwmdac { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pwmdac_pins>; ++ status = "okay"; ++}; ++ + &qspi { + #address-cells = <1>; + #size-cells = <0>; +@@ -336,6 +415,46 @@ + }; + }; + ++ i2srx_pins: i2srx-0 { ++ clk-sd-pins { ++ pinmux = , ++ , ++ , ++ , ++ ; ++ input-enable; ++ }; ++ }; ++ ++ i2stx1_pins: i2stx1-0 { ++ sd-pins { ++ pinmux = ; ++ bias-disable; ++ input-disable; ++ }; ++ }; ++ ++ mclk_ext_pins: mclk-ext-0 { ++ mclk-ext-pins { ++ pinmux = ; ++ input-enable; ++ }; ++ }; ++ + mmc0_pins: mmc0-0 { + rst-pins { + pinmux = ; + }; + }; ++ ++ pcie0_pins: pcie0-0 { ++ clkreq-pins { ++ pinmux = ; ++ bias-pull-down; ++ drive-strength = <2>; ++ input-enable; ++ input-schmitt-disable; ++ slew-rate = <0>; ++ }; ++ ++ wake-pins { ++ pinmux = ; ++ bias-pull-up; ++ drive-strength = <2>; ++ input-enable; ++ input-schmitt-disable; ++ slew-rate = <0>; ++ }; ++ }; ++ ++ pcie1_pins: pcie1-0 { ++ clkreq-pins { ++ pinmux = ; ++ bias-pull-down; ++ drive-strength = <2>; ++ input-enable; ++ input-schmitt-disable; ++ slew-rate = <0>; ++ }; ++ ++ wake-pins { ++ pinmux = ; ++ bias-pull-up; ++ drive-strength = <2>; ++ input-enable; ++ input-schmitt-disable; ++ slew-rate = <0>; ++ }; ++ }; ++ ++ pwm_pins: pwm-0 { ++ pwm-pins { ++ pinmux = , ++ ; ++ bias-disable; ++ drive-strength = <12>; ++ input-disable; ++ input-schmitt-disable; ++ slew-rate = <0>; ++ }; ++ }; ++ ++ pwmdac_pins: pwmdac-0 { ++ pwmdac-pins { ++ pinmux = , ++ ; ++ bias-disable; ++ drive-strength = <2>; ++ input-disable; ++ input-schmitt-disable; ++ slew-rate = <0>; ++ }; ++ }; + + spi0_pins: spi0-0 { + mosi-pins { +--- a/arch/riscv/boot/dts/starfive/jh7110.dtsi ++++ b/arch/riscv/boot/dts/starfive/jh7110.dtsi +@@ -244,6 +244,7 @@ + clock-output-names = "dvp_clk"; + #clock-cells = <0>; + }; ++ + gmac0_rgmii_rxin: gmac0-rgmii-rxin-clock { + compatible = "fixed-clock"; + clock-output-names = "gmac0_rgmii_rxin"; +@@ -512,6 +513,43 @@ + status = "disabled"; + }; + ++ pwmdac: pwmdac@100b0000 { ++ compatible = "starfive,jh7110-pwmdac"; ++ reg = <0x0 0x100b0000 0x0 0x1000>; ++ clocks = <&syscrg JH7110_SYSCLK_PWMDAC_APB>, ++ <&syscrg JH7110_SYSCLK_PWMDAC_CORE>; ++ clock-names = "apb", "core"; ++ resets = <&syscrg JH7110_SYSRST_PWMDAC_APB>; ++ dmas = <&dma 22>; ++ dma-names = "tx"; ++ #sound-dai-cells = <0>; ++ status = "disabled"; ++ }; ++ ++ i2srx: i2s@100e0000 { ++ compatible = "starfive,jh7110-i2srx"; ++ reg = <0x0 0x100e0000 0x0 0x1000>; ++ clocks = <&syscrg JH7110_SYSCLK_I2SRX_BCLK_MST>, ++ <&syscrg JH7110_SYSCLK_I2SRX_APB>, ++ <&syscrg JH7110_SYSCLK_MCLK>, ++ <&syscrg JH7110_SYSCLK_MCLK_INNER>, ++ <&mclk_ext>, ++ <&syscrg JH7110_SYSCLK_I2SRX_BCLK>, ++ <&syscrg JH7110_SYSCLK_I2SRX_LRCK>, ++ <&i2srx_bclk_ext>, ++ <&i2srx_lrck_ext>; ++ clock-names = "i2sclk", "apb", "mclk", ++ "mclk_inner", "mclk_ext", "bclk", ++ "lrck", "bclk_ext", "lrck_ext"; ++ resets = <&syscrg JH7110_SYSRST_I2SRX_APB>, ++ <&syscrg JH7110_SYSRST_I2SRX_BCLK>; ++ dmas = <0>, <&dma 24>; ++ dma-names = "tx", "rx"; ++ starfive,syscon = <&sys_syscon 0x18 0x2>; ++ #sound-dai-cells = <0>; ++ status = "disabled"; ++ }; ++ + usb0: usb@10100000 { + compatible = "starfive,jh7110-usb"; + ranges = <0x0 0x0 0x10100000 0x100000>; +@@ -736,6 +774,56 @@ + status = "disabled"; + }; + ++ i2stx0: i2s@120b0000 { ++ compatible = "starfive,jh7110-i2stx0"; ++ reg = <0x0 0x120b0000 0x0 0x1000>; ++ clocks = <&syscrg JH7110_SYSCLK_I2STX0_BCLK_MST>, ++ <&syscrg JH7110_SYSCLK_I2STX0_APB>, ++ <&syscrg JH7110_SYSCLK_MCLK>, ++ <&syscrg JH7110_SYSCLK_MCLK_INNER>, ++ <&mclk_ext>; ++ clock-names = "i2sclk", "apb", "mclk", ++ "mclk_inner","mclk_ext"; ++ resets = <&syscrg JH7110_SYSRST_I2STX0_APB>, ++ <&syscrg JH7110_SYSRST_I2STX0_BCLK>; ++ dmas = <&dma 47>; ++ dma-names = "tx"; ++ #sound-dai-cells = <0>; ++ status = "disabled"; ++ }; ++ ++ i2stx1: i2s@120c0000 { ++ compatible = "starfive,jh7110-i2stx1"; ++ reg = <0x0 0x120c0000 0x0 0x1000>; ++ clocks = <&syscrg JH7110_SYSCLK_I2STX1_BCLK_MST>, ++ <&syscrg JH7110_SYSCLK_I2STX1_APB>, ++ <&syscrg JH7110_SYSCLK_MCLK>, ++ <&syscrg JH7110_SYSCLK_MCLK_INNER>, ++ <&mclk_ext>, ++ <&syscrg JH7110_SYSCLK_I2STX1_BCLK>, ++ <&syscrg JH7110_SYSCLK_I2STX1_LRCK>, ++ <&i2stx_bclk_ext>, ++ <&i2stx_lrck_ext>; ++ clock-names = "i2sclk", "apb", "mclk", ++ "mclk_inner", "mclk_ext", "bclk", ++ "lrck", "bclk_ext", "lrck_ext"; ++ resets = <&syscrg JH7110_SYSRST_I2STX1_APB>, ++ <&syscrg JH7110_SYSRST_I2STX1_BCLK>; ++ dmas = <&dma 48>; ++ dma-names = "tx"; ++ #sound-dai-cells = <0>; ++ status = "disabled"; ++ }; ++ ++ pwm: pwm@120d0000 { ++ compatible = "starfive,jh7110-pwm", "opencores,pwm-v1"; ++ reg = <0x0 0x120d0000 0x0 0x10000>; ++ clocks = <&syscrg JH7110_SYSCLK_PWM_APB>; ++ resets = <&syscrg JH7110_SYSRST_PWM_APB>; ++ #pwm-cells = <3>; ++ status = "disabled"; ++ }; ++ + sfctemp: temperature-sensor@120e0000 { + compatible = "starfive,jh7110-temp"; + reg = <0x0 0x120e0000 0x0 0x10000>; +@@ -811,6 +899,26 @@ + #gpio-cells = <2>; + }; + ++ timer@13050000 { ++ compatible = "starfive,jh7110-timer"; ++ reg = <0x0 0x13050000 0x0 0x10000>; ++ interrupts = <69>, <70>, <71>, <72>; ++ clocks = <&syscrg JH7110_SYSCLK_TIMER_APB>, ++ <&syscrg JH7110_SYSCLK_TIMER0>, ++ <&syscrg JH7110_SYSCLK_TIMER1>, ++ <&syscrg JH7110_SYSCLK_TIMER2>, ++ <&syscrg JH7110_SYSCLK_TIMER3>; ++ clock-names = "apb", "ch0", "ch1", ++ "ch2", "ch3"; ++ resets = <&syscrg JH7110_SYSRST_TIMER_APB>, ++ <&syscrg JH7110_SYSRST_TIMER0>, ++ <&syscrg JH7110_SYSRST_TIMER1>, ++ <&syscrg JH7110_SYSRST_TIMER2>, ++ <&syscrg JH7110_SYSRST_TIMER3>; ++ reset-names = "apb", "ch0", "ch1", ++ "ch2", "ch3"; ++ }; ++ + watchdog@13070000 { + compatible = "starfive,jh7110-wdt"; + reg = <0x0 0x13070000 0x0 0x10000>; +@@ -1011,6 +1119,32 @@ + #power-domain-cells = <1>; + }; + ++ csi2rx: csi-bridge@19800000 { ++ compatible = "starfive,jh7110-csi2rx"; ++ reg = <0x0 0x19800000 0x0 0x10000>; ++ clocks = <&ispcrg JH7110_ISPCLK_VIN_SYS>, ++ <&ispcrg JH7110_ISPCLK_VIN_APB>, ++ <&ispcrg JH7110_ISPCLK_VIN_PIXEL_IF0>, ++ <&ispcrg JH7110_ISPCLK_VIN_PIXEL_IF1>, ++ <&ispcrg JH7110_ISPCLK_VIN_PIXEL_IF2>, ++ <&ispcrg JH7110_ISPCLK_VIN_PIXEL_IF3>; ++ clock-names = "sys_clk", "p_clk", ++ "pixel_if0_clk", "pixel_if1_clk", ++ "pixel_if2_clk", "pixel_if3_clk"; ++ resets = <&ispcrg JH7110_ISPRST_VIN_SYS>, ++ <&ispcrg JH7110_ISPRST_VIN_APB>, ++ <&ispcrg JH7110_ISPRST_VIN_PIXEL_IF0>, ++ <&ispcrg JH7110_ISPRST_VIN_PIXEL_IF1>, ++ <&ispcrg JH7110_ISPRST_VIN_PIXEL_IF2>, ++ <&ispcrg JH7110_ISPRST_VIN_PIXEL_IF3>; ++ reset-names = "sys", "reg_bank", ++ "pixel_if0", "pixel_if1", ++ "pixel_if2", "pixel_if3"; ++ phys = <&csi_phy>; ++ phy-names = "dphy"; ++ status = "disabled"; ++ }; ++ + ispcrg: clock-controller@19810000 { + compatible = "starfive,jh7110-ispcrg"; + reg = <0x0 0x19810000 0x0 0x10000>; +@@ -1028,6 +1162,19 @@ + power-domains = <&pwrc JH7110_PD_ISP>; + }; + ++ csi_phy: phy@19820000 { ++ compatible = "starfive,jh7110-dphy-rx"; ++ reg = <0x0 0x19820000 0x0 0x10000>; ++ clocks = <&ispcrg JH7110_ISPCLK_M31DPHY_CFG_IN>, ++ <&ispcrg JH7110_ISPCLK_M31DPHY_REF_IN>, ++ <&ispcrg JH7110_ISPCLK_M31DPHY_TX_ESC_LAN0>; ++ clock-names = "cfg", "ref", "tx"; ++ resets = <&ispcrg JH7110_ISPRST_M31DPHY_HW>, ++ <&ispcrg JH7110_ISPRST_M31DPHY_B09_AON>; ++ power-domains = <&aon_syscon JH7110_AON_PD_DPHY_RX>; ++ #phy-cells = <0>; ++ }; ++ + voutcrg: clock-controller@295c0000 { + compatible = "starfive,jh7110-voutcrg"; + reg = <0x0 0x295c0000 0x0 0x10000>; +@@ -1045,5 +1192,91 @@ + #reset-cells = <1>; + power-domains = <&pwrc JH7110_PD_VOUT>; + }; ++ ++ pcie0: pcie@940000000 { ++ compatible = "starfive,jh7110-pcie"; ++ reg = <0x9 0x40000000 0x0 0x1000000>, ++ <0x0 0x2b000000 0x0 0x100000>; ++ reg-names = "cfg", "apb"; ++ linux,pci-domain = <0>; ++ #address-cells = <3>; ++ #size-cells = <2>; ++ #interrupt-cells = <1>; ++ ranges = <0x82000000 0x0 0x30000000 0x0 0x30000000 0x0 0x08000000>, ++ <0xc3000000 0x9 0x00000000 0x9 0x00000000 0x0 0x40000000>; ++ interrupts = <56>; ++ interrupt-map-mask = <0x0 0x0 0x0 0x7>; ++ interrupt-map = <0x0 0x0 0x0 0x1 &pcie_intc0 0x1>, ++ <0x0 0x0 0x0 0x2 &pcie_intc0 0x2>, ++ <0x0 0x0 0x0 0x3 &pcie_intc0 0x3>, ++ <0x0 0x0 0x0 0x4 &pcie_intc0 0x4>; ++ msi-controller; ++ device_type = "pci"; ++ starfive,stg-syscon = <&stg_syscon>; ++ bus-range = <0x0 0xff>; ++ clocks = <&syscrg JH7110_SYSCLK_NOC_BUS_STG_AXI>, ++ <&stgcrg JH7110_STGCLK_PCIE0_TL>, ++ <&stgcrg JH7110_STGCLK_PCIE0_AXI_MST0>, ++ <&stgcrg JH7110_STGCLK_PCIE0_APB>; ++ clock-names = "noc", "tl", "axi_mst0", "apb"; ++ resets = <&stgcrg JH7110_STGRST_PCIE0_AXI_MST0>, ++ <&stgcrg JH7110_STGRST_PCIE0_AXI_SLV0>, ++ <&stgcrg JH7110_STGRST_PCIE0_AXI_SLV>, ++ <&stgcrg JH7110_STGRST_PCIE0_BRG>, ++ <&stgcrg JH7110_STGRST_PCIE0_CORE>, ++ <&stgcrg JH7110_STGRST_PCIE0_APB>; ++ reset-names = "mst0", "slv0", "slv", "brg", ++ "core", "apb"; ++ status = "disabled"; ++ ++ pcie_intc0: interrupt-controller { ++ #address-cells = <0>; ++ #interrupt-cells = <1>; ++ interrupt-controller; ++ }; ++ }; ++ ++ pcie1: pcie@9c0000000 { ++ compatible = "starfive,jh7110-pcie"; ++ reg = <0x9 0xc0000000 0x0 0x1000000>, ++ <0x0 0x2c000000 0x0 0x100000>; ++ reg-names = "cfg", "apb"; ++ linux,pci-domain = <1>; ++ #address-cells = <3>; ++ #size-cells = <2>; ++ #interrupt-cells = <1>; ++ ranges = <0x82000000 0x0 0x38000000 0x0 0x38000000 0x0 0x08000000>, ++ <0xc3000000 0x9 0x80000000 0x9 0x80000000 0x0 0x40000000>; ++ interrupts = <57>; ++ interrupt-map-mask = <0x0 0x0 0x0 0x7>; ++ interrupt-map = <0x0 0x0 0x0 0x1 &pcie_intc1 0x1>, ++ <0x0 0x0 0x0 0x2 &pcie_intc1 0x2>, ++ <0x0 0x0 0x0 0x3 &pcie_intc1 0x3>, ++ <0x0 0x0 0x0 0x4 &pcie_intc1 0x4>; ++ msi-controller; ++ device_type = "pci"; ++ starfive,stg-syscon = <&stg_syscon>; ++ bus-range = <0x0 0xff>; ++ clocks = <&syscrg JH7110_SYSCLK_NOC_BUS_STG_AXI>, ++ <&stgcrg JH7110_STGCLK_PCIE1_TL>, ++ <&stgcrg JH7110_STGCLK_PCIE1_AXI_MST0>, ++ <&stgcrg JH7110_STGCLK_PCIE1_APB>; ++ clock-names = "noc", "tl", "axi_mst0", "apb"; ++ resets = <&stgcrg JH7110_STGRST_PCIE1_AXI_MST0>, ++ <&stgcrg JH7110_STGRST_PCIE1_AXI_SLV0>, ++ <&stgcrg JH7110_STGRST_PCIE1_AXI_SLV>, ++ <&stgcrg JH7110_STGRST_PCIE1_BRG>, ++ <&stgcrg JH7110_STGRST_PCIE1_CORE>, ++ <&stgcrg JH7110_STGRST_PCIE1_APB>; ++ reset-names = "mst0", "slv0", "slv", "brg", ++ "core", "apb"; ++ status = "disabled"; ++ ++ pcie_intc1: interrupt-controller { ++ #address-cells = <0>; ++ #interrupt-cells = <1>; ++ interrupt-controller; ++ }; ++ }; + }; + }; diff --git a/target/linux/starfive/patches-6.6/0049-MAINTAINERS-Update-all-StarFive-entries.patch b/target/linux/starfive/patches-6.6/0049-MAINTAINERS-Update-all-StarFive-entries.patch new file mode 100644 index 0000000000..a7a832dce2 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0049-MAINTAINERS-Update-all-StarFive-entries.patch @@ -0,0 +1,147 @@ +From ae7b57a0c69953f5ec06a378aedeed4c86637998 Mon Sep 17 00:00:00 2001 +From: Hal Feng +Date: Tue, 11 Apr 2023 16:25:57 +0800 +Subject: [PATCH 049/116] MAINTAINERS: Update all StarFive entries + +Merge all StarFive maintainers changes together. + +Signed-off-by: Hal Feng +--- + MAINTAINERS | 61 +++++++++++++++++++++++++++++++++++++++++++++++++---- + 1 file changed, 57 insertions(+), 4 deletions(-) + +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -7053,6 +7053,14 @@ T: git git://anongit.freedesktop.org/drm + F: Documentation/devicetree/bindings/display/rockchip/ + F: drivers/gpu/drm/rockchip/ + ++DRM DRIVERS FOR STARFIVE ++M: Keith Zhao ++L: dri-devel@lists.freedesktop.org ++S: Maintained ++T: git git://anongit.freedesktop.org/drm/drm-misc ++F: Documentation/devicetree/bindings/display/starfive/ ++F: drivers/gpu/drm/verisilicon/ ++ + DRM DRIVERS FOR STI + M: Alain Volmat + L: dri-devel@lists.freedesktop.org +@@ -16016,6 +16024,13 @@ F: Documentation/i2c/busses/i2c-ocores.r + F: drivers/i2c/busses/i2c-ocores.c + F: include/linux/platform_data/i2c-ocores.h + ++OPENCORES PWM DRIVER ++M: William Qiu ++M: Hal Feng ++S: Supported ++F: Documentation/devicetree/bindings/pwm/opencores,pwm.yaml ++F: drivers/pwm/pwm-ocores.c ++ + OPENRISC ARCHITECTURE + M: Jonas Bonn + M: Stefan Kristiansson +@@ -16427,6 +16442,14 @@ S: Maintained + F: Documentation/devicetree/bindings/pci/layerscape-pcie-gen4.txt + F: drivers/pci/controller/mobiveil/pcie-layerscape-gen4.c + ++PCI DRIVER FOR PLDA PCIE IP ++M: Daire McNamara ++M: Kevin Xie ++L: linux-pci@vger.kernel.org ++S: Maintained ++F: Documentation/devicetree/bindings/pci/plda,* ++F: drivers/pci/controller/plda/*plda* ++ + PCI DRIVER FOR RENESAS R-CAR + M: Marek Vasut + M: Yoshihiro Shimoda +@@ -16658,7 +16681,7 @@ M: Daire McNamara +@@ -16682,6 +16705,13 @@ S: Maintained + F: Documentation/devicetree/bindings/pci/socionext,uniphier-pcie* + F: drivers/pci/controller/dwc/pcie-uniphier* + ++PCIE DRIVER FOR STARFIVE JH71x0 ++M: Kevin Xie ++L: linux-pci@vger.kernel.org ++S: Maintained ++F: Documentation/devicetree/bindings/pci/starfive* ++F: drivers/pci/controller/plda/pcie-starfive.c ++ + PCIE DRIVER FOR ST SPEAR13XX + M: Pratyush Anand + L: linux-pci@vger.kernel.org +@@ -18454,7 +18484,7 @@ F: drivers/char/hw_random/mpfs-rng.c + F: drivers/clk/microchip/clk-mpfs*.c + F: drivers/i2c/busses/i2c-microchip-corei2c.c + F: drivers/mailbox/mailbox-mpfs.c +-F: drivers/pci/controller/pcie-microchip-host.c ++F: drivers/pci/controller/plda/pcie-microchip-host.c + F: drivers/pwm/pwm-microchip-core.c + F: drivers/reset/reset-mpfs.c + F: drivers/rtc/rtc-mpfs.c +@@ -20435,6 +20465,15 @@ M: Ion Badulescu + S: Odd Fixes + F: drivers/net/ethernet/adaptec/starfire* + ++STARFIVE CAMERA SUBSYSTEM DRIVER ++M: Jack Zhu ++M: Changhuang Liang ++L: linux-media@vger.kernel.org ++S: Maintained ++F: Documentation/admin-guide/media/starfive_camss.rst ++F: Documentation/devicetree/bindings/media/starfive,jh7110-camss.yaml ++F: drivers/staging/media/starfive/camss ++ + STARFIVE CRYPTO DRIVER + M: Jia Jie Ho + M: William Qiu +@@ -20473,6 +20512,13 @@ S: Supported + F: Documentation/devicetree/bindings/clock/starfive,jh7110-pll.yaml + F: drivers/clk/starfive/clk-starfive-jh7110-pll.c + ++STARFIVE JH7110 PWMDAC DRIVER ++M: Hal Feng ++M: Xingyu Wu ++S: Supported ++F: Documentation/devicetree/bindings/sound/starfive,jh7110-pwmdac.yaml ++F: sound/soc/starfive/jh7110_pwmdac.c ++ + STARFIVE JH7110 SYSCON + M: William Qiu + M: Xingyu Wu +@@ -20520,9 +20566,10 @@ F: drivers/usb/cdns3/cdns3-starfive.c + + STARFIVE JH71XX PMU CONTROLLER DRIVER + M: Walker Chen ++M: Changhuang Liang + S: Supported + F: Documentation/devicetree/bindings/power/starfive* +-F: drivers/pmdomain/starfive/jh71xx-pmu.c ++F: drivers/pmdomain/starfive/ + F: include/dt-bindings/power/starfive,jh7110-pmu.h + + STARFIVE SOC DRIVERS +@@ -20530,7 +20577,13 @@ M: Conor Dooley + S: Maintained + T: git https://git.kernel.org/pub/scm/linux/kernel/git/conor/linux.git/ + F: Documentation/devicetree/bindings/soc/starfive/ +-F: drivers/soc/starfive/ ++ ++STARFIVE JH7110 TIMER DRIVER ++M: Samin Guo ++M: Xingyu Wu ++S: Supported ++F: Documentation/devicetree/bindings/timer/starfive,jh7110-timer.yaml ++F: drivers/clocksource/timer-jh7110.c + + STARFIVE TRNG DRIVER + M: Jia Jie Ho diff --git a/target/linux/starfive/patches-6.6/0050-mmc-starfive-Change-the-voltage-to-adapt-to-JH7110-E.patch b/target/linux/starfive/patches-6.6/0050-mmc-starfive-Change-the-voltage-to-adapt-to-JH7110-E.patch new file mode 100644 index 0000000000..a38a885894 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0050-mmc-starfive-Change-the-voltage-to-adapt-to-JH7110-E.patch @@ -0,0 +1,64 @@ +From e394195396995456ef98f52ac123c0cb64687748 Mon Sep 17 00:00:00 2001 +From: William Qiu +Date: Mon, 9 Oct 2023 10:59:03 +0800 +Subject: [PATCH 050/116] mmc: starfive: Change the voltage to adapt to JH7110 + EVB + +Change the voltage, so the driver can adapt to JH7110 EVB. + +Signed-off-by: William Qiu +Signed-off-by: Hal Feng +--- + drivers/mmc/host/dw_mmc-starfive.c | 30 ++++++++++++++++++++++++++++++ + 1 file changed, 30 insertions(+) + +--- a/drivers/mmc/host/dw_mmc-starfive.c ++++ b/drivers/mmc/host/dw_mmc-starfive.c +@@ -8,6 +8,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -95,10 +96,39 @@ out: + return ret; + } + ++static int dw_mci_starfive_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios) ++{ ++ ++ struct dw_mci_slot *slot = mmc_priv(mmc); ++ struct dw_mci *host = slot->host; ++ u32 ret; ++ ++ if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) ++ ret = gpio_direction_output(25, 0); ++ else if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180) ++ ret = gpio_direction_output(25, 1); ++ if (ret) ++ return ret; ++ ++ if (!IS_ERR(mmc->supply.vqmmc)) { ++ ret = mmc_regulator_set_vqmmc(mmc, ios); ++ if (ret < 0) { ++ dev_err(host->dev, "Regulator set error %d\n", ret); ++ return ret; ++ } ++ } ++ ++ /* We should delay 20ms wait for timing setting finished. */ ++ mdelay(20); ++ ++ return 0; ++} ++ + static const struct dw_mci_drv_data starfive_data = { + .common_caps = MMC_CAP_CMD23, + .set_ios = dw_mci_starfive_set_ios, + .execute_tuning = dw_mci_starfive_execute_tuning, ++ .switch_voltage = dw_mci_starfive_switch_voltage, + }; + + static const struct of_device_id dw_mci_starfive_match[] = { diff --git a/target/linux/starfive/patches-6.6/0051-spi-spl022-Get-and-deassert-reset-in-probe.patch b/target/linux/starfive/patches-6.6/0051-spi-spl022-Get-and-deassert-reset-in-probe.patch new file mode 100644 index 0000000000..485ab0d9e1 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0051-spi-spl022-Get-and-deassert-reset-in-probe.patch @@ -0,0 +1,60 @@ +From 2cd3e51cb76d49d8db6274ebdc1ba1eb5c872f10 Mon Sep 17 00:00:00 2001 +From: "ziv.xu" +Date: Sun, 4 Feb 2024 10:35:24 +0800 +Subject: [PATCH 051/116] spi: spl022: Get and deassert reset in probe() + +This fix spi1~6 communication time out. + +Signed-off-by: ziv.xu +Signed-off-by: Hal Feng +--- + drivers/spi/spi-pl022.c | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +--- a/drivers/spi/spi-pl022.c ++++ b/drivers/spi/spi-pl022.c +@@ -33,6 +33,7 @@ + #include + #include + #include ++#include + + /* + * This macro is used to define some register default values. +@@ -370,6 +371,7 @@ struct pl022 { + resource_size_t phybase; + void __iomem *virtbase; + struct clk *clk; ++ struct reset_control *rst; + struct spi_controller *host; + struct pl022_ssp_controller *host_info; + /* Message per-transfer pump */ +@@ -2181,6 +2183,19 @@ static int pl022_probe(struct amba_devic + goto err_no_clk_en; + } + ++ pl022->rst = devm_reset_control_get(&adev->dev, NULL); ++ if (IS_ERR(pl022->rst)) { ++ status = PTR_ERR(pl022->rst); ++ dev_err(&adev->dev, "could not retrieve SSP/SPI bus reset\n"); ++ goto err_no_rst; ++ } ++ ++ status = reset_control_deassert(pl022->rst); ++ if (status) { ++ dev_err(&adev->dev, "could not deassert SSP/SPI bus reset\n"); ++ goto err_no_rst_de; ++ } ++ + /* Initialize transfer pump */ + tasklet_init(&pl022->pump_transfers, pump_transfers, + (unsigned long)pl022); +@@ -2240,6 +2255,8 @@ static int pl022_probe(struct amba_devic + if (platform_info->enable_dma) + pl022_dma_remove(pl022); + err_no_irq: ++ err_no_rst_de: ++ err_no_rst: + clk_disable_unprepare(pl022->clk); + err_no_clk_en: + err_no_clk: diff --git a/target/linux/starfive/patches-6.6/0052-ASoC-dwc-i2s-Fix-getting-platform-data-error-for-Sta.patch b/target/linux/starfive/patches-6.6/0052-ASoC-dwc-i2s-Fix-getting-platform-data-error-for-Sta.patch new file mode 100644 index 0000000000..e274e84a25 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0052-ASoC-dwc-i2s-Fix-getting-platform-data-error-for-Sta.patch @@ -0,0 +1,30 @@ +From 9cc8de0cdc1600f460f618e342e1f524adad07c4 Mon Sep 17 00:00:00 2001 +From: Xingyu Wu +Date: Wed, 21 Feb 2024 10:23:48 +0800 +Subject: [PATCH 052/116] ASoC: dwc: i2s: Fix getting platform data error for + StarFive JH7110 + +JH7110 need to use a DT specific function to get the platform data, +otherwise, it fails in probe(). + +Fixes: 9c97790a07dc ("ASoC: dwc: Fix non-DT instantiation") +Signed-off-by: Xingyu Wu +Signed-off-by: Hal Feng +--- + sound/soc/dwc/dwc-i2s.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/sound/soc/dwc/dwc-i2s.c ++++ b/sound/soc/dwc/dwc-i2s.c +@@ -917,7 +917,11 @@ static int jh7110_i2stx0_clk_cfg(struct + + static int dw_i2s_probe(struct platform_device *pdev) + { ++#ifdef CONFIG_OF ++ const struct i2s_platform_data *pdata = of_device_get_match_data(&pdev->dev); ++#else + const struct i2s_platform_data *pdata = pdev->dev.platform_data; ++#endif + struct dw_i2s_dev *dev; + struct resource *res; + int ret, irq; diff --git a/target/linux/starfive/patches-6.6/0053-ASoC-dwc-i2s-Add-RX-master-support-for-StarFive-JH71.patch b/target/linux/starfive/patches-6.6/0053-ASoC-dwc-i2s-Add-RX-master-support-for-StarFive-JH71.patch new file mode 100644 index 0000000000..8203beca52 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0053-ASoC-dwc-i2s-Add-RX-master-support-for-StarFive-JH71.patch @@ -0,0 +1,67 @@ +From 1be9bd37fdb5f50162dba0158e1fee295ebca9aa Mon Sep 17 00:00:00 2001 +From: Xingyu Wu +Date: Tue, 17 Oct 2023 17:22:52 +0800 +Subject: [PATCH 053/116] ASoC: dwc: i2s: Add RX master support for StarFive + JH7110 SoC + +Add JH7110 I2S RX master support, so the PDM can work on JH7110 +EVB board. + +Signed-off-by: Xingyu Wu +Signed-off-by: Hal Feng +--- + sound/soc/dwc/dwc-i2s.c | 31 +++++++++++++++++++++++++++++++ + 1 file changed, 31 insertions(+) + +--- a/sound/soc/dwc/dwc-i2s.c ++++ b/sound/soc/dwc/dwc-i2s.c +@@ -906,6 +906,27 @@ static int jh7110_i2srx_crg_init(struct + return jh7110_i2s_crg_slave_init(dev); + } + ++/* Special syscon initialization about RX channel with master mode on JH7110 SoC */ ++static int jh7110_i2srx_mst_crg_init(struct dw_i2s_dev *dev) ++{ ++ struct regmap *regmap; ++ unsigned int args[5]; ++ ++ regmap = syscon_regmap_lookup_by_phandle_args(dev->dev->of_node, ++ "starfive,syscon", ++ 5, args); ++ if (IS_ERR(regmap)) ++ return dev_err_probe(dev->dev, PTR_ERR(regmap), "getting the regmap failed\n"); ++ ++ /* Enable I2Srx with syscon register, args[0]: offset, args[1]: mask */ ++ regmap_update_bits(regmap, args[0], args[1], args[1]); ++ ++ /* Change I2Srx source (PDM) with syscon register, args[0]: offset, args[1]: mask */ ++ regmap_update_bits(regmap, args[2], args[3], args[4]); ++ ++ return jh7110_i2s_crg_master_init(dev); ++} ++ + static int jh7110_i2stx0_clk_cfg(struct i2s_clk_config_data *config) + { + struct dw_i2s_dev *dev = container_of(config, struct dw_i2s_dev, config); +@@ -1086,11 +1107,21 @@ static const struct i2s_platform_data jh + .i2s_pd_init = jh7110_i2srx_crg_init, + }; + ++static const struct i2s_platform_data jh7110_i2srx_mst_data = { ++ .cap = DWC_I2S_RECORD | DW_I2S_MASTER, ++ .channel = TWO_CHANNEL_SUPPORT, ++ .snd_fmts = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, ++ .snd_rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000, ++ .i2s_clk_cfg = jh7110_i2stx0_clk_cfg, ++ .i2s_pd_init = jh7110_i2srx_mst_crg_init, ++}; ++ + static const struct of_device_id dw_i2s_of_match[] = { + { .compatible = "snps,designware-i2s", }, + { .compatible = "starfive,jh7110-i2stx0", .data = &jh7110_i2stx0_data, }, + { .compatible = "starfive,jh7110-i2stx1", .data = &jh7110_i2stx1_data,}, + { .compatible = "starfive,jh7110-i2srx", .data = &jh7110_i2srx_data,}, ++ { .compatible = "starfive,jh7110-i2srx-master", .data = &jh7110_i2srx_mst_data,}, + {}, + }; + diff --git a/target/linux/starfive/patches-6.6/0054-pinctrl-starfive-jh7110-Unset-.strict-in-pinmux_ops.patch b/target/linux/starfive/patches-6.6/0054-pinctrl-starfive-jh7110-Unset-.strict-in-pinmux_ops.patch new file mode 100644 index 0000000000..51976c656e --- /dev/null +++ b/target/linux/starfive/patches-6.6/0054-pinctrl-starfive-jh7110-Unset-.strict-in-pinmux_ops.patch @@ -0,0 +1,24 @@ +From 1ec26ba377d8ae59cd09811ec78623a750a9c150 Mon Sep 17 00:00:00 2001 +From: Hal Feng +Date: Mon, 26 Feb 2024 11:35:44 +0800 +Subject: [PATCH 054/116] pinctrl: starfive: jh7110: Unset .strict in + pinmux_ops + +Allow simultaneous use of the same pin for GPIO and another function. +This feature is used in HDMI hot plug detect. + +Signed-off-by: Hal Feng +--- + drivers/pinctrl/starfive/pinctrl-starfive-jh7110.c | 1 - + 1 file changed, 1 deletion(-) + +--- a/drivers/pinctrl/starfive/pinctrl-starfive-jh7110.c ++++ b/drivers/pinctrl/starfive/pinctrl-starfive-jh7110.c +@@ -327,7 +327,6 @@ static const struct pinmux_ops jh7110_pi + .get_function_name = pinmux_generic_get_function_name, + .get_function_groups = pinmux_generic_get_function_groups, + .set_mux = jh7110_set_mux, +- .strict = true, + }; + + static const u8 jh7110_drive_strength_mA[4] = { 2, 4, 8, 12 }; diff --git a/target/linux/starfive/patches-6.6/0055-mm-pgtable-generic.c-Export-symbol-__pte_offset_map.patch b/target/linux/starfive/patches-6.6/0055-mm-pgtable-generic.c-Export-symbol-__pte_offset_map.patch new file mode 100644 index 0000000000..39699e8b97 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0055-mm-pgtable-generic.c-Export-symbol-__pte_offset_map.patch @@ -0,0 +1,22 @@ +From 005549a2bd839335b0e3dc4152f00f642b524f07 Mon Sep 17 00:00:00 2001 +From: Hal Feng +Date: Sat, 7 Oct 2023 18:10:20 +0800 +Subject: [PATCH 055/116] mm/pgtable-generic.c: Export symbol __pte_offset_map + +So JH7110 vdec can call pte_offset_map() when it is built as a module. + +Signed-off-by: Hal Feng +--- + mm/pgtable-generic.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/mm/pgtable-generic.c ++++ b/mm/pgtable-generic.c +@@ -304,6 +304,7 @@ nomap: + rcu_read_unlock(); + return NULL; + } ++EXPORT_SYMBOL(__pte_offset_map); + + pte_t *pte_offset_map_nolock(struct mm_struct *mm, pmd_t *pmd, + unsigned long addr, spinlock_t **ptlp) diff --git a/target/linux/starfive/patches-6.6/0056-riscv-dts-starfive-Add-JH7110-EVB-device-tree.patch b/target/linux/starfive/patches-6.6/0056-riscv-dts-starfive-Add-JH7110-EVB-device-tree.patch new file mode 100644 index 0000000000..7b803d3997 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0056-riscv-dts-starfive-Add-JH7110-EVB-device-tree.patch @@ -0,0 +1,2623 @@ +From 9f46b0d43f8945ff3a8b81ddc6567df370b60911 Mon Sep 17 00:00:00 2001 +From: Hal Feng +Date: Fri, 28 Jul 2023 17:19:12 +0800 +Subject: [PATCH 056/116] riscv: dts: starfive: Add JH7110 EVB device tree + +Add JH7110 evaluation board device tree. +The code is ported from tag JH7110_SDK_6.1_v5.11.3 + +Signed-off-by: Hal Feng +--- + arch/riscv/boot/dts/starfive/Makefile | 3 + + arch/riscv/boot/dts/starfive/jh7110-clk.dtsi | 80 ++ + .../boot/dts/starfive/jh7110-evb-pinctrl.dtsi | 997 ++++++++++++++++++ + arch/riscv/boot/dts/starfive/jh7110-evb.dts | 35 + + arch/riscv/boot/dts/starfive/jh7110-evb.dtsi | 854 +++++++++++++++ + arch/riscv/boot/dts/starfive/jh7110.dtsi | 482 ++++++++- + 6 files changed, 2444 insertions(+), 7 deletions(-) + create mode 100644 arch/riscv/boot/dts/starfive/jh7110-clk.dtsi + create mode 100644 arch/riscv/boot/dts/starfive/jh7110-evb-pinctrl.dtsi + create mode 100644 arch/riscv/boot/dts/starfive/jh7110-evb.dts + create mode 100644 arch/riscv/boot/dts/starfive/jh7110-evb.dtsi + +--- a/arch/riscv/boot/dts/starfive/Makefile ++++ b/arch/riscv/boot/dts/starfive/Makefile +@@ -4,9 +4,12 @@ DTC_FLAGS_jh7100-beaglev-starlight := -@ + DTC_FLAGS_jh7100-starfive-visionfive-v1 := -@ + DTC_FLAGS_jh7110-starfive-visionfive-2-v1.2a := -@ + DTC_FLAGS_jh7110-starfive-visionfive-2-v1.3b := -@ ++DTC_FLAGS_jh7110-evb := -@ + + dtb-$(CONFIG_ARCH_STARFIVE) += jh7100-beaglev-starlight.dtb + dtb-$(CONFIG_ARCH_STARFIVE) += jh7100-starfive-visionfive-v1.dtb + + dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-starfive-visionfive-2-v1.2a.dtb + dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-starfive-visionfive-2-v1.3b.dtb ++ ++dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-evb.dtb +--- /dev/null ++++ b/arch/riscv/boot/dts/starfive/jh7110-clk.dtsi +@@ -0,0 +1,80 @@ ++// SPDX-License-Identifier: GPL-2.0 OR MIT ++/* ++ * Copyright (C) 2023 StarFive Technology Co., Ltd. ++ */ ++ ++/ { ++ ac108_mclk: ac108_mclk { ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <24000000>; ++ }; ++ ++ clk_ext_camera: clk-ext-camera { ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <24000000>; ++ }; ++ ++ wm8960_mclk: wm8960_mclk { ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <24576000>; ++ }; ++}; ++ ++&dvp_clk { ++ clock-frequency = <74250000>; ++}; ++ ++&gmac0_rgmii_rxin { ++ clock-frequency = <125000000>; ++}; ++ ++&gmac0_rmii_refin { ++ clock-frequency = <50000000>; ++}; ++ ++&gmac1_rgmii_rxin { ++ clock-frequency = <125000000>; ++}; ++ ++&gmac1_rmii_refin { ++ clock-frequency = <50000000>; ++}; ++ ++&hdmitx0_pixelclk { ++ clock-frequency = <297000000>; ++}; ++ ++&i2srx_bclk_ext { ++ clock-frequency = <12288000>; ++}; ++ ++&i2srx_lrck_ext { ++ clock-frequency = <192000>; ++}; ++ ++&i2stx_bclk_ext { ++ clock-frequency = <12288000>; ++}; ++ ++&i2stx_lrck_ext { ++ clock-frequency = <192000>; ++}; ++ ++&mclk_ext { ++ clock-frequency = <12288000>; ++}; ++ ++&osc { ++ clock-frequency = <24000000>; ++}; ++ ++&rtc_osc { ++ clock-frequency = <32768>; ++}; ++ ++&tdm_ext { ++ clock-frequency = <49152000>; ++}; +--- /dev/null ++++ b/arch/riscv/boot/dts/starfive/jh7110-evb-pinctrl.dtsi +@@ -0,0 +1,997 @@ ++// SPDX-License-Identifier: GPL-2.0 OR MIT ++/* ++ * Copyright (C) 2023 StarFive Technology Co., Ltd. ++ * Author: Hal Feng ++ */ ++ ++#include "jh7110-pinfunc.h" ++ ++&sysgpio { ++ can0_pins: can0-0 { ++ can-pins { ++ pinmux = , ++ , ++ ; ++ input-enable; ++ }; ++ }; ++ ++ can1_pins: can1-0 { ++ can-pins { ++ pinmux = , ++ , ++ ; ++ drive-strength = <12>; ++ input-enable; ++ }; ++ }; ++ ++ dvp_pins: dvp-0 { ++ dvp-pins{ ++ pinmux = , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ ; ++ input-enable; ++ }; ++ }; ++ ++ emmc0_pins: emmc0-0 { ++ emmc-pins { ++ pinmux = , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ ; ++ bias-pull-up; ++ drive-strength = <12>; ++ input-enable; ++ slew-rate = <1>; ++ }; ++ }; ++ ++ emmc1_pins: emmc1-0 { ++ emmc-pins { ++ pinmux = , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ ; ++ bias-pull-up; ++ input-enable; ++ }; ++ }; ++ ++ gmac0_pins: gmac0-0 { ++ reset-pins { ++ pinmux = ; ++ bias-pull-up; ++ }; ++ }; ++ ++ gmac1_pins: gmac1-0 { ++ mdc-pins { ++ pinmux = ; ++ }; ++ }; ++ ++ hdmi_pins: hdmi-0 { ++ scl-pins { ++ pinmux = ; ++ bias-pull-up; ++ input-enable; ++ }; ++ ++ sda-pins { ++ pinmux = ; ++ bias-pull-up; ++ input-enable; ++ }; ++ ++ cec-pins { ++ pinmux = ; ++ bias-pull-up; ++ input-enable; ++ }; ++ ++ hpd-pins { ++ pinmux = ; ++ bias-disable; /* external pull-up */ ++ input-enable; ++ }; ++ }; ++ ++ i2c0_pins: i2c0-0 { ++ i2c-pins { ++ pinmux = , ++ ; ++ bias-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-pull-up; ++ input-enable; ++ input-schmitt-enable; ++ }; ++ }; ++ ++ i2c3_pins: i2c3-0 { ++ i2c-pins { ++ pinmux = , ++ ; ++ bias-pull-up; ++ input-enable; ++ input-schmitt-enable; ++ }; ++ }; ++ ++ i2c4_pins: i2c4-0 { ++ i2c-pins { ++ pinmux = , ++ ; ++ bias-pull-up; ++ input-enable; ++ input-schmitt-enable; ++ }; ++ }; ++ ++ i2c5_pins: i2c5-0 { ++ i2c-pins { ++ pinmux = , ++ ; ++ bias-pull-up; ++ input-enable; ++ input-schmitt-enable; ++ }; ++ }; ++ ++ i2c6_pins: i2c6-0 { ++ i2c-pins { ++ pinmux = , ++ ; ++ bias-pull-up; ++ input-enable; ++ input-schmitt-enable; ++ }; ++ }; ++ ++ i2s_clk_pins: i2s-clk-0 { ++ bclk-lrck-pins { ++ pinmux = , ++ , ++ , ++ ; ++ input-enable; ++ }; ++ }; ++ ++ i2srx_clk_pins: i2srx-clk-0 { ++ mclk-pins { ++ pinmux = ; ++ input-enable; ++ }; ++ }; ++ ++ i2srx_pins: i2srx-0 { ++ i2srx-pins { ++ pinmux = ; ++ input-enable; ++ }; ++ }; ++ ++ i2stx_pins: i2stx-0 { ++ i2stx-pins { ++ pinmux = ; ++ input-enable; ++ }; ++ }; ++ ++ mclk_ext_pins: mclk-ext-0 { ++ mclk-ext-pins { ++ pinmux = ; ++ input-enable; ++ }; ++ }; ++ ++ pdm_pins: pdm-0 { ++ pdm-pins { ++ pinmux = , ++ ; ++ input-enable; ++ }; ++ }; ++ ++ pwm_ch0to3_pins: pwm-ch0to3-0 { ++ pwm-pins { ++ pinmux = , ++ , ++ , ++ ; ++ drive-strength = <12>; ++ }; ++ }; ++ ++ pwmdac_pins: pwmdac-0 { ++ pwmdac-pins { ++ pinmux = , ++ ; ++ }; ++ }; ++ ++ rgb_pad_pins: rgb-pad-pins { ++ rgb-0-pins { ++ pinmux = ; ++ drive-strength = <12>; ++ input-disable; ++ slew-rate = <1>; ++ }; ++ ++ rgb-pins { ++ pinmux = , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ ; ++ drive-strength = <12>; ++ input-disable; ++ }; ++ }; ++ ++ sdcard0_pins: sdcard0-0 { ++ sdcard-pins { ++ pinmux = , ++ , ++ , ++ , ++ , ++ , ++ ; ++ bias-pull-up; ++ drive-strength = <12>; ++ input-enable; ++ slew-rate = <1>; ++ }; ++ }; ++ ++ sdcard1_pins: sdcard1-0 { ++ sdcard-pins { ++ pinmux = , ++ , ++ , ++ , ++ , ++ ; ++ bias-pull-up; ++ input-enable; ++ }; ++ }; ++ ++ spdif_pins: spdif-0 { ++ spdif-pins { ++ pinmux = ; ++ bias-pull-up; ++ input-enable; ++ }; ++ }; ++ ++ spi0_pins: spi0-0 { ++ mosi-pins { ++ pinmux = ; ++ bias-disable; ++ input-disable; ++ input-schmitt-disable; ++ }; ++ ++ miso-pins { ++ pinmux = ; ++ bias-pull-up; ++ input-enable; ++ input-schmitt-enable; ++ }; ++ ++ sck-pins { ++ pinmux = ; ++ bias-disable; ++ input-disable; ++ input-schmitt-disable; ++ }; ++ ++ ss-pins { ++ pinmux = ; ++ bias-disable; ++ input-disable; ++ input-schmitt-disable; ++ }; ++ }; ++ ++ spi1_pins: spi1-0 { ++ mosi-pins { ++ pinmux = ; ++ bias-disable; ++ input-disable; ++ input-schmitt-disable; ++ }; ++ ++ miso-pins { ++ pinmux = ; ++ bias-pull-up; ++ input-enable; ++ input-schmitt-enable; ++ }; ++ ++ sck-pins { ++ pinmux = ; ++ bias-disable; ++ input-disable; ++ input-schmitt-disable; ++ }; ++ ++ ss-pins { ++ pinmux = ; ++ bias-disable; ++ input-disable; ++ input-schmitt-disable; ++ }; ++ }; ++ ++ spi2_pins: spi2-0 { ++ mosi-pins { ++ pinmux = ; ++ bias-disable; ++ input-disable; ++ input-schmitt-disable; ++ }; ++ ++ miso-pins { ++ pinmux = ; ++ bias-pull-up; ++ input-enable; ++ input-schmitt-enable; ++ }; ++ ++ sck-pins { ++ pinmux = ; ++ bias-disable; ++ input-disable; ++ input-schmitt-disable; ++ }; ++ ++ ss-pins { ++ pinmux = ; ++ bias-disable; ++ input-disable; ++ input-schmitt-disable; ++ }; ++ }; ++ ++ spi3_pins: spi3-0 { ++ mosi-pins { ++ pinmux = ; ++ bias-disable; ++ input-disable; ++ input-schmitt-disable; ++ }; ++ ++ miso-pins { ++ pinmux = ; ++ bias-pull-up; ++ input-enable; ++ input-schmitt-enable; ++ }; ++ ++ sck-pins { ++ pinmux = ; ++ bias-disable; ++ input-disable; ++ input-schmitt-disable; ++ }; ++ ++ ss-pins { ++ pinmux = ; ++ bias-disable; ++ input-disable; ++ input-schmitt-disable; ++ }; ++ }; ++ ++ spi4_pins: spi4-0 { ++ mosi-pins { ++ pinmux = ; ++ bias-disable; ++ input-disable; ++ input-schmitt-disable; ++ }; ++ ++ miso-pins { ++ pinmux = ; ++ bias-pull-up; ++ input-enable; ++ input-schmitt-enable; ++ }; ++ ++ sck-pins { ++ pinmux = ; ++ bias-disable; ++ input-disable; ++ input-schmitt-disable; ++ }; ++ ++ ss-pins { ++ pinmux = ; ++ bias-disable; ++ input-disable; ++ input-schmitt-disable; ++ }; ++ }; ++ ++ spi5_pins: spi5-0 { ++ mosi-pins { ++ pinmux = ; ++ bias-disable; ++ input-disable; ++ input-schmitt-disable; ++ }; ++ ++ miso-pins { ++ pinmux = ; ++ bias-pull-up; ++ input-enable; ++ input-schmitt-enable; ++ }; ++ ++ sck-pins { ++ pinmux = ; ++ bias-disable; ++ input-disable; ++ input-schmitt-disable; ++ }; ++ ++ ss-pins { ++ pinmux = ; ++ bias-disable; ++ input-disable; ++ input-schmitt-disable; ++ }; ++ }; ++ ++ spi6_pins: spi6-0 { ++ mosi-pins { ++ pinmux = ; ++ bias-disable; ++ input-disable; ++ input-schmitt-disable; ++ }; ++ ++ miso-pins { ++ pinmux = ; ++ bias-pull-up; ++ input-enable; ++ input-schmitt-enable; ++ }; ++ ++ sck-pins { ++ pinmux = ; ++ bias-disable; ++ input-disable; ++ input-schmitt-disable; ++ }; ++ ++ ss-pins { ++ pinmux = ; ++ bias-disable; ++ input-disable; ++ input-schmitt-disable; ++ }; ++ }; ++ ++ tdm_pins: tdm-0 { ++ tx-pins { ++ pinmux = ; ++ bias-pull-up; ++ drive-strength = <2>; ++ input-disable; ++ input-schmitt-disable; ++ slew-rate = <0>; ++ }; ++ ++ rx-pins { ++ pinmux = ; ++ input-enable; ++ }; ++ ++ sync-pins { ++ pinmux = ; ++ input-enable; ++ }; ++ ++ pcmclk-pins { ++ pinmux = ; ++ input-enable; ++ }; ++ }; ++ ++ uart0_pins: uart0-0 { ++ tx-pins { ++ pinmux = ; ++ bias-disable; ++ drive-strength = <12>; ++ input-disable; ++ input-schmitt-disable; ++ slew-rate = <0>; ++ }; ++ ++ rx-pins { ++ pinmux = ; ++ bias-pull-up; ++ drive-strength = <2>; ++ input-enable; ++ input-schmitt-enable; ++ slew-rate = <0>; ++ }; ++ }; ++ ++ uart1_pins: uart1-0 { ++ tx-pins { ++ pinmux = ; ++ bias-disable; ++ drive-strength = <12>; ++ input-disable; ++ input-schmitt-disable; ++ slew-rate = <0>; ++ }; ++ ++ rx-pins { ++ pinmux = ; ++ bias-pull-up; ++ drive-strength = <2>; ++ input-enable; ++ input-schmitt-enable; ++ slew-rate = <0>; ++ }; ++ ++ cts-pins { ++ pinmux = ; ++ input-enable; ++ }; ++ ++ rts-pins { ++ pinmux = ; ++ input-enable; ++ }; ++ }; ++ ++ uart2_pins: uart2-0 { ++ tx-pins { ++ pinmux = ; ++ bias-disable; ++ drive-strength = <12>; ++ input-disable; ++ input-schmitt-disable; ++ slew-rate = <0>; ++ }; ++ ++ rx-pins { ++ pinmux = ; ++ bias-pull-up; ++ drive-strength = <2>; ++ input-enable; ++ input-schmitt-enable; ++ slew-rate = <0>; ++ }; ++ ++ cts-pins { ++ pinmux = ; ++ input-enable; ++ }; ++ ++ rts-pins { ++ pinmux = ; ++ input-enable; ++ }; ++ }; ++ ++ uart3_pins: uart3-0 { ++ tx-pins { ++ pinmux = ; ++ bias-disable; ++ drive-strength = <12>; ++ input-disable; ++ input-schmitt-disable; ++ slew-rate = <0>; ++ }; ++ ++ rx-pins { ++ pinmux = ; ++ bias-pull-up; ++ drive-strength = <2>; ++ input-enable; ++ input-schmitt-enable; ++ slew-rate = <0>; ++ }; ++ }; ++ ++ uart4_pins: uart4-0 { ++ tx-pins { ++ pinmux = ; ++ bias-disable; ++ drive-strength = <12>; ++ input-disable; ++ input-schmitt-disable; ++ slew-rate = <0>; ++ }; ++ ++ rx-pins { ++ pinmux = ; ++ bias-pull-up; ++ drive-strength = <2>; ++ input-enable; ++ input-schmitt-enable; ++ slew-rate = <0>; ++ }; ++ ++ cts-pins { ++ pinmux = ; ++ input-enable; ++ }; ++ ++ rts-pins { ++ pinmux = ; ++ input-enable; ++ }; ++ }; ++ ++ uart5_pins: uart5-0 { ++ tx-pins { ++ pinmux = ; ++ bias-disable; ++ drive-strength = <12>; ++ input-disable; ++ input-schmitt-disable; ++ slew-rate = <0>; ++ }; ++ ++ rx-pins { ++ pinmux = ; ++ bias-pull-up; ++ drive-strength = <2>; ++ input-enable; ++ input-schmitt-enable; ++ slew-rate = <0>; ++ }; ++ ++ cts-pins { ++ pinmux = ; ++ input-enable; ++ }; ++ ++ rts-pins { ++ pinmux = ; ++ input-enable; ++ }; ++ }; ++ ++ usb_pins: usb-0 { ++ usb-pins { ++ pinmux = , ++ ; ++ input-enable; ++ }; ++ }; ++}; ++ ++&aongpio { ++ pwm_ch4to5_pins: pwm-ch4to5-0 { ++ pwm-pins { ++ pinmux = , ++ ; ++ drive-strength = <12>; ++ }; ++ }; ++ ++ pwm_ch6to7_pins: pwm-ch6to7-0 { ++ pwm-pins { ++ pinmux = , ++ ; ++ drive-strength = <12>; ++ }; ++ }; ++}; +--- /dev/null ++++ b/arch/riscv/boot/dts/starfive/jh7110-evb.dts +@@ -0,0 +1,35 @@ ++// SPDX-License-Identifier: GPL-2.0 OR MIT ++/* ++ * Copyright (C) 2023 StarFive Technology Co., Ltd. ++ */ ++ ++/dts-v1/; ++#include "jh7110-evb.dtsi" ++ ++/ { ++ model = "StarFive JH7110 EVB"; ++ compatible = "starfive,jh7110-evb", "starfive,jh7110"; ++}; ++ ++&mmc0 { ++ assigned-clocks = <&syscrg JH7110_SYSCLK_SDIO0_SDCARD>; ++ assigned-clock-rates = <50000000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&sdcard0_pins>; ++ max-frequency = <100000000>; ++ card-detect-delay = <300>; ++ bus-width = <4>; ++ no-sdio; ++ no-mmc; ++ broken-cd; ++ post-power-on-delay-ms = <200>; ++ status = "okay"; ++}; ++ ++&pcie1 { ++ status = "okay"; ++}; ++ ++&usb0 { ++ status = "okay"; ++}; +--- /dev/null ++++ b/arch/riscv/boot/dts/starfive/jh7110-evb.dtsi +@@ -0,0 +1,854 @@ ++// SPDX-License-Identifier: GPL-2.0 OR MIT ++/* ++ * Copyright (C) 2023 StarFive Technology Co., Ltd. ++ */ ++ ++/dts-v1/; ++#include "jh7110.dtsi" ++#include "jh7110-clk.dtsi" ++#include "jh7110-evb-pinctrl.dtsi" ++#include ++ ++/ { ++ aliases { ++ ethernet0 = &gmac0; ++ ethernet1 = &gmac1; ++ i2c0 = &i2c0; ++ i2c1 = &i2c1; ++ i2c2 = &i2c2; ++ i2c3 = &i2c3; ++ i2c4 = &i2c4; ++ i2c5 = &i2c5; ++ i2c6 = &i2c6; ++ pcie0 = &pcie0; ++ pcie1 = &pcie1; ++ serial0 = &uart0; ++ serial3 = &uart3; ++ }; ++ ++ chosen { ++ stdout-path = "serial0:115200n8"; ++ }; ++ ++ cpus { ++ timebase-frequency = <4000000>; ++ }; ++ ++ memory@40000000 { ++ device_type = "memory"; ++ reg = <0x0 0x40000000 0x1 0x0>; ++ }; ++ ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ linux,cma { ++ compatible = "shared-dma-pool"; ++ reusable; ++ size = <0x0 0x20000000>; ++ alignment = <0x0 0x1000>; ++ alloc-ranges = <0x0 0x70000000 0x0 0x20000000>; ++ linux,cma-default; ++ }; ++ ++ e24_mem: e24@c0000000 { ++ reg = <0x0 0x6ce00000 0x0 0x1600000>; ++ }; ++ ++ xrp_reserved: xrpbuffer@f0000000 { ++ reg = <0x0 0x69c00000 0x0 0x01ffffff ++ 0x0 0x6bc00000 0x0 0x00001000 ++ 0x0 0x6bc01000 0x0 0x00fff000 ++ 0x0 0x6cc00000 0x0 0x00001000>; ++ }; ++ }; ++ ++ /* i2s + hdmi */ ++ sound1: snd-card1 { ++ compatible = "simple-audio-card"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ simple-audio-card,name = "StarFive-HDMI-Sound-Card"; ++ simple-audio-card,dai-link@0 { ++ reg = <0>; ++ format = "i2s"; ++ bitclock-master = <&sndi2s0>; ++ frame-master = <&sndi2s0>; ++ mclk-fs = <256>; ++ status = "okay"; ++ ++ sndi2s0: cpu { ++ sound-dai = <&i2stx0>; ++ }; ++ ++ codec { ++ sound-dai = <&hdmi>; ++ }; ++ }; ++ }; ++}; ++ ++&U74_1 { ++ /delete-property/ clocks; ++ /delete-property/ clock-names; ++}; ++ ++&U74_2 { ++ /delete-property/ clocks; ++ /delete-property/ clock-names; ++}; ++ ++&U74_3 { ++ /delete-property/ clocks; ++ /delete-property/ clock-names; ++}; ++ ++&U74_4 { ++ /delete-property/ clocks; ++ /delete-property/ clock-names; ++}; ++ ++&can0 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&can0_pins>; ++ status = "disabled"; ++}; ++ ++&can1 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&can1_pins>; ++ status = "disabled"; ++}; ++ ++&co_process { ++ memory-region = <&e24_mem>; ++ status = "okay"; ++}; ++ ++&dc8200 { ++ status = "okay"; ++ ++ dc_out: port { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ dc_out_dpi0: endpoint@0 { ++ reg = <0>; ++ remote-endpoint = <&hdmi_input0>; ++ }; ++ dc_out_dpi1: endpoint@1 { ++ reg = <1>; ++ remote-endpoint = <&hdmi_in_lcdc>; ++ }; ++ dc_out_dpi2: endpoint@2 { ++ reg = <2>; ++ remote-endpoint = <&mipi_in>; ++ }; ++ }; ++}; ++ ++&display { ++ ports = <&dc_out_dpi0>; ++ status = "okay"; ++}; ++ ++&dsi_output { ++ status = "okay"; ++ ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ port@0 { ++ reg = <0>; ++ mipi_in: endpoint { ++ remote-endpoint = <&dc_out_dpi2>; ++ }; ++ }; ++ ++ port@1 { ++ reg = <1>; ++ mipi_out: endpoint { ++ remote-endpoint = <&dsi_in_port>; ++ }; ++ }; ++ }; ++}; ++ ++&gmac0 { ++ phy-handle = <&phy0>; ++ phy-mode = "rgmii-id"; ++ status = "okay"; ++ ++ mdio { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ compatible = "snps,dwmac-mdio"; ++ ++ phy0: ethernet-phy@0 { ++ reg = <0>; ++ rx-internal-delay-ps = <1900>; ++ tx-internal-delay-ps = <1650>; ++ }; ++ }; ++}; ++ ++&gmac1 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "okay"; ++ ++ phy1: ethernet-phy@1 { ++ reg = <0>; ++ rxc-skew-ps = <1060>; ++ txc-skew-ps = <1800>; ++ }; ++}; ++ ++&gpu { ++ status = "okay"; ++}; ++ ++&hdmi { ++ status = "okay"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&hdmi_pins>; ++ hpd-gpio = <&sysgpio 15 GPIO_ACTIVE_HIGH>; ++ ++ hdmi_in: port { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ hdmi_in_lcdc: endpoint@0 { ++ reg = <0>; ++ remote-endpoint = <&dc_out_dpi1>; ++ }; ++ }; ++}; ++ ++&i2c0 { ++ clock-frequency = <100000>; ++ i2c-sda-hold-time-ns = <300>; ++ i2c-sda-falling-time-ns = <510>; ++ i2c-scl-falling-time-ns = <510>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2c0_pins>; ++ status = "disabled"; ++ ++ wm8960: codec@1a { ++ compatible = "wlf,wm8960"; ++ reg = <0x1a>; ++ wlf,shared-lrclk; ++ #sound-dai-cells = <0>; ++ }; ++ ++ ac108: ac108@3b { ++ compatible = "x-power,ac108_0"; ++ reg = <0x3b>; ++ #sound-dai-cells = <0>; ++ data-protocol = <0>; ++ }; ++}; ++ ++&i2c1 { ++ clock-frequency = <100000>; ++ i2c-sda-hold-time-ns = <300>; ++ i2c-sda-falling-time-ns = <510>; ++ i2c-scl-falling-time-ns = <510>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2c1_pins>; ++ status = "disabled"; ++}; ++ ++&i2c2 { ++ clock-frequency = <100000>; ++ i2c-sda-hold-time-ns = <300>; ++ i2c-sda-falling-time-ns = <510>; ++ i2c-scl-falling-time-ns = <510>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2c2_pins>; ++ status = "okay"; ++ ++ tinker_ft5406: tinker_ft5406@38 { ++ compatible = "tinker_ft5406"; ++ reg = <0x38>; ++ }; ++ ++ seeed_plane_i2c@45 { ++ compatible = "seeed_panel"; ++ reg = <0x45>; ++ ++ port { ++ panel_dsi_port: endpoint { ++ remote-endpoint = <&dsi_out_port>; ++ }; ++ }; ++ }; ++}; ++ ++&i2c3 { ++ clock-frequency = <100000>; ++ i2c-sda-hold-time-ns = <300>; ++ i2c-sda-falling-time-ns = <510>; ++ i2c-scl-falling-time-ns = <510>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2c3_pins>; ++ status = "disabled"; ++}; ++ ++&i2c4 { ++ clock-frequency = <100000>; ++ i2c-sda-hold-time-ns = <300>; ++ i2c-sda-falling-time-ns = <510>; ++ i2c-scl-falling-time-ns = <510>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2c4_pins>; ++ status = "okay"; ++ ++ sc2235: sc2235@30 { ++ compatible = "smartsens,sc2235"; ++ reg = <0x30>; ++ clocks = <&clk_ext_camera>; ++ clock-names = "xclk"; ++ ++ port { ++ /* Parallel bus endpoint */ ++ sc2235_to_parallel: endpoint { ++ remote-endpoint = <¶llel_from_sc2235>; ++ bus-type = <5>; /* Parallel */ ++ bus-width = <8>; ++ data-shift = <2>; /* lines 13:6 are used */ ++ hsync-active = <1>; ++ vsync-active = <1>; ++ pclk-sample = <1>; ++ }; ++ }; ++ }; ++ ++ tda998x@70 { ++ compatible = "nxp,tda998x"; ++ reg = <0x70>; ++ ++ port { ++ tda998x_0_input: endpoint { ++ remote-endpoint = <&hdmi_out>; ++ }; ++ }; ++ }; ++}; ++ ++&i2c5 { ++ clock-frequency = <100000>; ++ i2c-sda-hold-time-ns = <300>; ++ i2c-sda-falling-time-ns = <510>; ++ i2c-scl-falling-time-ns = <510>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2c5_pins>; ++ status = "okay"; ++ ++ pmic: jh7110_evb_reg@50 { ++ compatible = "starfive,jh7110-evb-regulator"; ++ reg = <0x50>; ++ ++ regulators { ++ hdmi_1p8: LDO_REG1 { ++ regulator-name = "hdmi_1p8"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ }; ++ mipitx_1p8: LDO_REG2 { ++ regulator-name = "mipitx_1p8"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ }; ++ mipirx_1p8: LDO_REG3 { ++ regulator-name = "mipirx_1p8"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ }; ++ hdmi_0p9: LDO_REG4 { ++ regulator-name = "hdmi_0p9"; ++ regulator-min-microvolt = <900000>; ++ regulator-max-microvolt = <900000>; ++ }; ++ mipitx_0p9: LDO_REG5 { ++ regulator-name = "mipitx_0p9"; ++ regulator-min-microvolt = <900000>; ++ regulator-max-microvolt = <900000>; ++ }; ++ mipirx_0p9: LDO_REG6 { ++ regulator-name = "mipirx_0p9"; ++ regulator-min-microvolt = <900000>; ++ regulator-max-microvolt = <900000>; ++ }; ++ sdio_vdd: LDO_REG7 { ++ regulator-name = "sdio_vdd"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <3300000>; ++ }; ++ }; ++ }; ++}; ++ ++&i2c6 { ++ clock-frequency = <100000>; ++ i2c-sda-hold-time-ns = <300>; ++ i2c-sda-falling-time-ns = <510>; ++ i2c-scl-falling-time-ns = <510>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2c6_pins>; ++ status = "okay"; ++ ++ ov4689: ov4689@36 { ++ compatible = "ovti,ov4689"; ++ reg = <0x36>; ++ clocks = <&clk_ext_camera>; ++ clock-names = "xclk"; ++ //reset-gpio = <&sysgpio 18 0>; ++ rotation = <180>; ++ ++ port { ++ /* Parallel bus endpoint */ ++ ov4689_to_csi2rx0: endpoint { ++ remote-endpoint = <&csi2rx0_from_ov4689>; ++ bus-type = <4>; /* MIPI CSI-2 D-PHY */ ++ clock-lanes = <0>; ++ data-lanes = <1 2 3 4>; ++ }; ++ }; ++ }; ++ ++ imx219: imx219@10 { ++ compatible = "sony,imx219"; ++ reg = <0x10>; ++ clocks = <&clk_ext_camera>; ++ clock-names = "xclk"; ++ reset-gpio = <&sysgpio 10 0>; ++ //DOVDD-supply = <&v2v8>; ++ rotation = <0>; ++ orientation = <1>; //CAMERA_ORIENTATION_BACK ++ ++ port { ++ /* CSI2 bus endpoint */ ++ imx219_to_csi2rx0: endpoint { ++ remote-endpoint = <&csi2rx0_from_imx219>; ++ bus-type = <4>; /* MIPI CSI-2 D-PHY */ ++ clock-lanes = <0>; ++ data-lanes = <2 1>; ++ lane-polarities = <1 1 1>; ++ link-frequencies = /bits/ 64 <456000000>; ++ }; ++ }; ++ }; ++ ++ imx708: imx708@1a { ++ compatible = "sony,imx708"; ++ reg = <0x1a>; ++ clocks = <&clk_ext_camera>; ++ reset-gpio = <&sysgpio 10 0>; ++ ++ port { ++ imx708_to_csi2rx0: endpoint { ++ remote-endpoint = <&csi2rx0_from_imx708>; ++ data-lanes = <1 2>; ++ clock-noncontinuous; ++ link-frequencies = /bits/ 64 <450000000>; ++ }; ++ }; ++ }; ++}; ++ ++&i2srx { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2s_clk_pins &i2srx_pins>; ++}; ++ ++&i2srx_mst { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2srx_clk_pins>; ++}; ++ ++&i2stx0 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&mclk_ext_pins>; ++ status = "okay"; ++}; ++ ++&i2stx1 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2stx_pins>; ++}; ++ ++&jpu { ++ status = "okay"; ++}; ++ ++&mailbox_contrl0 { ++ status = "okay"; ++}; ++ ++&mailbox_client0 { ++ status = "okay"; ++}; ++ ++&mipi_dphy { ++ status = "okay"; ++}; ++ ++&mipi_dsi { ++ status = "okay"; ++ ++ port { ++ dsi_out_port: endpoint@0 { ++ remote-endpoint = <&panel_dsi_port>; ++ }; ++ dsi_in_port: endpoint@1 { ++ remote-endpoint = <&mipi_out>; ++ }; ++ }; ++ ++ mipi_panel: panel@0 { ++ /*compatible = "";*/ ++ status = "okay"; ++ }; ++}; ++ ++&pcie0 { ++ enable-gpios = <&sysgpio 32 GPIO_ACTIVE_HIGH>; ++ perst-gpios = <&sysgpio 26 GPIO_ACTIVE_LOW>; ++ phys = <&pciephy0>; ++ status = "disabled"; ++}; ++ ++&pcie1 { ++ enable-gpios = <&sysgpio 21 GPIO_ACTIVE_HIGH>; ++ perst-gpios = <&sysgpio 28 GPIO_ACTIVE_LOW>; ++ phys = <&pciephy1>; ++ status = "disabled"; ++}; ++ ++&pciephy0 { ++ starfive,sys-syscon = <&sys_syscon 0x18>; ++ starfive,stg-syscon = <&stg_syscon 0x148 0x1f4>; ++}; ++ ++&pdm { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pdm_pins>; ++ status = "disabled"; ++}; ++ ++&pwmdac { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pwmdac_pins>; ++}; ++ ++&qspi { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "okay"; ++ ++ nor_flash: flash@0 { ++ compatible = "jedec,spi-nor"; ++ reg=<0>; ++ cdns,read-delay = <5>; ++ spi-max-frequency = <4687500>; ++ cdns,tshsl-ns = <1>; ++ cdns,tsd2d-ns = <1>; ++ cdns,tchsh-ns = <1>; ++ cdns,tslch-ns = <1>; ++ ++ partitions { ++ compatible = "fixed-partitions"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ spl@0 { ++ reg = <0x0 0x40000>; ++ }; ++ uboot@100000 { ++ reg = <0x100000 0x300000>; ++ }; ++ data@f00000 { ++ reg = <0xf00000 0x100000>; ++ }; ++ }; ++ }; ++}; ++ ++&rgb_output { ++ status = "okay"; ++ ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ port@0 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <0>; ++ ++ hdmi_input0:endpoint@0 { ++ reg = <0>; ++ remote-endpoint = <&dc_out_dpi0>; ++ }; ++ }; ++ ++ port@1 { ++ reg = <1>; ++ ++ hdmi_out:endpoint { ++ remote-endpoint = <&tda998x_0_input>; ++ }; ++ }; ++ }; ++}; ++ ++&spdif { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&spdif_pins>; ++}; ++ ++&spi0 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&spi0_pins>; ++ status = "disabled"; ++ ++ spi_dev0: spi_dev@0 { ++ compatible = "rohm,dh2228fv"; ++ reg = <0>; ++ pl022,com-mode = <1>; ++ spi-max-frequency = <10000000>; ++ }; ++}; ++ ++&spi1 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&spi1_pins>; ++ status = "disabled"; ++ ++ spi_dev1: spi_dev@0 { ++ compatible = "rohm,dh2228fv"; ++ reg = <0>; ++ pl022,com-mode = <1>; ++ spi-max-frequency = <10000000>; ++ }; ++}; ++ ++&spi2 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&spi2_pins>; ++ status = "disabled"; ++ ++ spi_dev2: spi_dev@0 { ++ compatible = "rohm,dh2228fv"; ++ reg = <0>; ++ pl022,com-mode = <1>; ++ spi-max-frequency = <10000000>; ++ }; ++}; ++ ++&spi3 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&spi3_pins>; ++ status = "disabled"; ++ ++ spi_dev3: spi_dev@0 { ++ compatible = "rohm,dh2228fv"; ++ reg = <0>; ++ pl022,com-mode = <1>; ++ spi-max-frequency = <10000000>; ++ }; ++}; ++ ++&spi4 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&spi4_pins>; ++ status = "disabled"; ++ ++ spi_dev4: spi_dev@0 { ++ compatible = "rohm,dh2228fv"; ++ reg = <0>; ++ pl022,com-mode = <1>; ++ spi-max-frequency = <10000000>; ++ }; ++}; ++ ++&spi5 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&spi5_pins>; ++ status = "disabled"; ++ ++ spi_dev5: spi_dev@0 { ++ compatible = "rohm,dh2228fv"; ++ reg = <0>; ++ pl022,com-mode = <1>; ++ spi-max-frequency = <10000000>; ++ }; ++}; ++ ++&spi6 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&spi6_pins>; ++ status = "disabled"; ++ ++ spi_dev6: spi_dev@0 { ++ compatible = "rohm,dh2228fv"; ++ reg = <0>; ++ pl022,com-mode = <1>; ++ spi-max-frequency = <10000000>; ++ }; ++}; ++ ++&tda988x_pin { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&rgb_pad_pins>; ++ status = "disabled"; ++}; ++ ++&tdm { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&tdm_pins>; ++ status = "disabled"; ++}; ++ ++&uart0 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&uart0_pins>; ++ status = "okay"; ++}; ++ ++&uart1 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&uart1_pins>; ++ status = "disabled"; ++}; ++ ++&uart2 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&uart2_pins>; ++ status = "disabled"; ++}; ++ ++&uart3 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&uart3_pins>; ++ status = "disabled"; ++}; ++ ++&uart4 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&uart4_pins>; ++ status = "disabled"; ++}; ++ ++&uart5 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&uart5_pins>; ++ status = "disabled"; ++}; ++ ++&usb0 { ++ clocks = <&stgcrg JH7110_STGCLK_USB0_LPM>, ++ <&stgcrg JH7110_STGCLK_USB0_STB>, ++ <&stgcrg JH7110_STGCLK_USB0_APB>, ++ <&stgcrg JH7110_STGCLK_USB0_AXI>, ++ <&stgcrg JH7110_STGCLK_USB0_UTMI_APB>, ++ <&stgcrg JH7110_STGCLK_PCIE0_APB>; ++ clock-names = "lpm", "stb", "apb", "axi", "utmi_apb", "phy"; ++ resets = <&stgcrg JH7110_STGRST_USB0_PWRUP>, ++ <&stgcrg JH7110_STGRST_USB0_APB>, ++ <&stgcrg JH7110_STGRST_USB0_AXI>, ++ <&stgcrg JH7110_STGRST_USB0_UTMI_APB>, ++ <&stgcrg JH7110_STGRST_PCIE0_APB>; ++ reset-names = "pwrup", "apb", "axi", "utmi_apb", "phy"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&usb_pins>; ++ dr_mode = "host"; /* host or peripheral */ ++ status = "disabled"; ++}; ++ ++&usb_cdns3 { ++ phys = <&usbphy0>, <&pciephy0>; ++ phy-names = "cdns3,usb2-phy", "cdns3,usb3-phy"; ++}; ++ ++&vin_sysctl { ++ status = "okay"; ++ ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ port@0 { ++ reg = <0>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ /* Parallel bus endpoint */ ++ parallel_from_sc2235: endpoint@0 { ++ reg = <0>; ++ remote-endpoint = <&sc2235_to_parallel>; ++ bus-type = <5>; /* Parallel */ ++ bus-width = <8>; ++ data-shift = <2>; /* lines 9:2 are used */ ++ hsync-active = <1>; ++ vsync-active = <0>; ++ pclk-sample = <1>; ++ status = "okay"; ++ }; ++ }; ++ ++ port@1 { ++ reg = <1>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ /* CSI2 bus endpoint */ ++ csi2rx0_from_ov4689: endpoint@0 { ++ reg = <0>; ++ remote-endpoint = <&ov4689_to_csi2rx0>; ++ bus-type = <4>; /* MIPI CSI-2 D-PHY */ ++ clock-lanes = <0>; ++ data-lanes = <1 2 3 4>; ++ status = "okay"; ++ }; ++ ++ /* CSI2 bus endpoint */ ++ csi2rx0_from_imx219: endpoint@1 { ++ reg = <1>; ++ remote-endpoint = <&imx219_to_csi2rx0>; ++ bus-type = <4>; /* MIPI CSI-2 D-PHY */ ++ clock-lanes = <0>; ++ data-lanes = <2 1>; ++ lane-polarities = <1 1 1>; ++ status = "okay"; ++ }; ++ ++ csi2rx0_from_imx708: endpoint@2 { ++ reg = <2>; ++ remote-endpoint = <&imx708_to_csi2rx0>; ++ bus-type = <4>; /* MIPI CSI-2 D-PHY */ ++ clock-lanes = <0>; ++ data-lanes = <2 1>; ++ lane-polarities = <1 1 1>; ++ status = "okay"; ++ }; ++ }; ++ }; ++}; ++ ++&vpu_dec { ++ status = "okay"; ++}; ++ ++&vpu_enc { ++ status = "okay"; ++}; ++ ++&xrp { ++ memory-region = <&xrp_reserved>; ++ status = "okay"; ++}; +--- a/arch/riscv/boot/dts/starfive/jh7110.dtsi ++++ b/arch/riscv/boot/dts/starfive/jh7110.dtsi +@@ -196,11 +196,60 @@ + opp-750000000 { + opp-hz = /bits/ 64 <750000000>; + opp-microvolt = <800000>; ++ opp-suspend; + }; + opp-1500000000 { + opp-hz = /bits/ 64 <1500000000>; + opp-microvolt = <1040000>; + }; ++ /* CPU opp table for 1.25GHz */ ++ opp-312500000 { ++ opp-hz = /bits/ 64 <312500000>; ++ opp-microvolt = <800000>; ++ }; ++ opp-417000000 { ++ opp-hz = /bits/ 64 <417000000>; ++ opp-microvolt = <800000>; ++ }; ++ opp-625000000 { ++ opp-hz = /bits/ 64 <625000000>; ++ opp-microvolt = <800000>; ++ opp-suspend; ++ }; ++ opp-1250000000 { ++ opp-hz = /bits/ 64 <1250000000>; ++ opp-microvolt = <1000000>; ++ }; ++ }; ++ ++ display: display-subsystem { ++ compatible = "starfive,jh7110-display","verisilicon,display-subsystem"; ++ status = "disabled"; ++ }; ++ ++ dsi_output: dsi-output { ++ compatible = "starfive,jh7110-display-encoder","verisilicon,dsi-encoder"; ++ status = "disabled"; ++ }; ++ ++ mailbox_client0: mailbox_client { ++ compatible = "starfive,mailbox-test"; ++ mbox-names = "rx", "tx"; ++ mboxes = <&mailbox_contrl0 0 1>,<&mailbox_contrl0 1 0>; ++ status = "disabled"; ++ }; ++ ++ rgb_output: rgb-output { ++ compatible = "starfive,jh7110-rgb_output","verisilicon,rgb-encoder"; ++ //verisilicon,dss-syscon = <&dssctrl>; ++ //verisilicon,mux-mask = <0x70 0x380>; ++ //verisilicon,mux-val = <0x40 0x280>; ++ status = "disabled"; ++ }; ++ ++ tda988x_pin: tda988x_pin { ++ compatible = "starfive,tda998x_rgb_pin"; ++ status = "disabled"; + }; + + thermal-zones { +@@ -349,7 +398,9 @@ + + ccache: cache-controller@2010000 { + compatible = "starfive,jh7110-ccache", "sifive,ccache0", "cache"; +- reg = <0x0 0x2010000 0x0 0x4000>; ++ reg = <0x0 0x2010000 0x0 0x4000>, ++ <0x0 0x8000000 0x0 0x2000000>, ++ <0x0 0xa000000 0x0 0x2000000>; + interrupts = <1>, <3>, <4>, <2>; + cache-block-size = <64>; + cache-level = <2>; +@@ -378,7 +429,8 @@ + clocks = <&syscrg JH7110_SYSCLK_UART0_CORE>, + <&syscrg JH7110_SYSCLK_UART0_APB>; + clock-names = "baudclk", "apb_pclk"; +- resets = <&syscrg JH7110_SYSRST_UART0_APB>; ++ resets = <&syscrg JH7110_SYSRST_UART0_APB>, ++ <&syscrg JH7110_SYSRST_UART0_CORE>; + interrupts = <32>; + reg-io-width = <4>; + reg-shift = <2>; +@@ -391,7 +443,8 @@ + clocks = <&syscrg JH7110_SYSCLK_UART1_CORE>, + <&syscrg JH7110_SYSCLK_UART1_APB>; + clock-names = "baudclk", "apb_pclk"; +- resets = <&syscrg JH7110_SYSRST_UART1_APB>; ++ resets = <&syscrg JH7110_SYSRST_UART1_APB>, ++ <&syscrg JH7110_SYSRST_UART1_CORE>; + interrupts = <33>; + reg-io-width = <4>; + reg-shift = <2>; +@@ -404,7 +457,8 @@ + clocks = <&syscrg JH7110_SYSCLK_UART2_CORE>, + <&syscrg JH7110_SYSCLK_UART2_APB>; + clock-names = "baudclk", "apb_pclk"; +- resets = <&syscrg JH7110_SYSRST_UART2_APB>; ++ resets = <&syscrg JH7110_SYSRST_UART2_APB>, ++ <&syscrg JH7110_SYSRST_UART2_CORE>; + interrupts = <34>; + reg-io-width = <4>; + reg-shift = <2>; +@@ -513,6 +567,25 @@ + status = "disabled"; + }; + ++ spdif: spdif@100a0000 { ++ compatible = "starfive,jh7110-spdif"; ++ reg = <0x0 0x100a0000 0x0 0x1000>; ++ clocks = <&syscrg JH7110_SYSCLK_SPDIF_APB>, ++ <&syscrg JH7110_SYSCLK_SPDIF_CORE>, ++ <&syscrg JH7110_SYSCLK_AUDIO_ROOT>, ++ <&syscrg JH7110_SYSCLK_MCLK_INNER>, ++ <&mclk_ext>, <&syscrg JH7110_SYSCLK_MCLK>; ++ clock-names = "apb", "core", ++ "audroot", "mclk_inner", ++ "mclk_ext", "mclk"; ++ resets = <&syscrg JH7110_SYSRST_SPDIF_APB>; ++ reset-names = "apb"; ++ interrupts = <84>; ++ interrupt-names = "tx"; ++ #sound-dai-cells = <0>; ++ status = "disabled"; ++ }; ++ + pwmdac: pwmdac@100b0000 { + compatible = "starfive,jh7110-pwmdac"; + reg = <0x0 0x100b0000 0x0 0x1000>; +@@ -526,6 +599,42 @@ + status = "disabled"; + }; + ++ pdm: pdm@100d0000 { ++ compatible = "starfive,jh7110-pdm"; ++ reg = <0x0 0x100d0000 0x0 0x1000>; ++ reg-names = "pdm"; ++ clocks = <&syscrg JH7110_SYSCLK_PDM_DMIC>, ++ <&syscrg JH7110_SYSCLK_PDM_APB>, ++ <&syscrg JH7110_SYSCLK_MCLK>, ++ <&mclk_ext>; ++ clock-names = "pdm_mclk", "pdm_apb", ++ "clk_mclk", "mclk_ext"; ++ resets = <&syscrg JH7110_SYSRST_PDM_DMIC>, ++ <&syscrg JH7110_SYSRST_PDM_APB>; ++ reset-names = "pdm_dmic", "pdm_apb"; ++ #sound-dai-cells = <0>; ++ status = "disabled"; ++ }; ++ ++ i2srx_mst: i2srx_mst@100e0000 { ++ compatible = "starfive,jh7110-i2srx-master"; ++ reg = <0x0 0x100e0000 0x0 0x1000>; ++ clocks = <&syscrg JH7110_SYSCLK_I2SRX_BCLK_MST>, ++ <&syscrg JH7110_SYSCLK_I2SRX_APB>, ++ <&syscrg JH7110_SYSCLK_MCLK>, ++ <&syscrg JH7110_SYSCLK_MCLK_INNER>, ++ <&mclk_ext>; ++ clock-names = "i2sclk", "apb", "mclk", ++ "mclk_inner","mclk_ext"; ++ resets = <&syscrg JH7110_SYSRST_I2SRX_APB>, ++ <&syscrg JH7110_SYSRST_I2SRX_BCLK>; ++ dmas = <&dma 24>; ++ dma-names = "rx"; ++ starfive,syscon = <&sys_syscon 0x18 0x2 0x34 0x3FC00 0x24400>; ++ #sound-dai-cells = <0>; ++ status = "disabled"; ++ }; ++ + i2srx: i2s@100e0000 { + compatible = "starfive,jh7110-i2srx"; + reg = <0x0 0x100e0000 0x0 0x1000>; +@@ -622,6 +731,26 @@ + #reset-cells = <1>; + }; + ++ xrp: xrp@10230000 { ++ compatible = "cdns,xrp"; ++ dma-coherent; ++ reg = <0x0 0x10230000 0x0 0x00010000 ++ 0x0 0x10240000 0x0 0x00010000>; ++ clocks = <&stgcrg JH7110_STGCLK_HIFI4_CLK_CORE>; ++ clock-names = "core_clk"; ++ resets = <&stgcrg JH7110_STGRST_HIFI4_CORE>, ++ <&stgcrg JH7110_STGRST_HIFI4_AXI>; ++ reset-names = "rst_core","rst_axi"; ++ starfive,stg-syscon = <&stg_syscon>; ++ firmware-name = "hifi4_elf"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges = <0x40000000 0x0 0x20000000 0x040000 ++ 0x69c00000 0x0 0x69c00000 0x03000000>; ++ status = "disabled"; ++ dsp@0 {}; ++ }; ++ + stg_syscon: syscon@10240000 { + compatible = "starfive,jh7110-stg-syscon", "syscon"; + reg = <0x0 0x10240000 0x0 0x1000>; +@@ -633,7 +762,8 @@ + clocks = <&syscrg JH7110_SYSCLK_UART3_CORE>, + <&syscrg JH7110_SYSCLK_UART3_APB>; + clock-names = "baudclk", "apb_pclk"; +- resets = <&syscrg JH7110_SYSRST_UART3_APB>; ++ resets = <&syscrg JH7110_SYSRST_UART3_APB>, ++ <&syscrg JH7110_SYSRST_UART3_CORE>; + interrupts = <45>; + reg-io-width = <4>; + reg-shift = <2>; +@@ -646,7 +776,8 @@ + clocks = <&syscrg JH7110_SYSCLK_UART4_CORE>, + <&syscrg JH7110_SYSCLK_UART4_APB>; + clock-names = "baudclk", "apb_pclk"; +- resets = <&syscrg JH7110_SYSRST_UART4_APB>; ++ resets = <&syscrg JH7110_SYSRST_UART4_APB>, ++ <&syscrg JH7110_SYSRST_UART4_CORE>; + interrupts = <46>; + reg-io-width = <4>; + reg-shift = <2>; +@@ -659,7 +790,8 @@ + clocks = <&syscrg JH7110_SYSCLK_UART5_CORE>, + <&syscrg JH7110_SYSCLK_UART5_APB>; + clock-names = "baudclk", "apb_pclk"; +- resets = <&syscrg JH7110_SYSRST_UART5_APB>; ++ resets = <&syscrg JH7110_SYSRST_UART5_APB>, ++ <&syscrg JH7110_SYSRST_UART5_CORE>; + interrupts = <47>; + reg-io-width = <4>; + reg-shift = <2>; +@@ -919,6 +1051,18 @@ + "ch2", "ch3"; + }; + ++ mailbox_contrl0: mailbox@13060000 { ++ compatible = "starfive,mail_box"; ++ reg = <0x0 0x13060000 0x0 0x0001000>; ++ clocks = <&syscrg JH7110_SYSCLK_MAILBOX_APB>; ++ clock-names = "clk_apb"; ++ resets = <&syscrg JH7110_SYSRST_MAILBOX_APB>; ++ reset-names = "mbx_rre"; ++ interrupts = <26 27>; ++ #mbox-cells = <2>; ++ status = "disabled"; ++ }; ++ + watchdog@13070000 { + compatible = "starfive,jh7110-wdt"; + reg = <0x0 0x13070000 0x0 0x10000>; +@@ -929,6 +1073,112 @@ + <&syscrg JH7110_SYSRST_WDT_CORE>; + }; + ++ jpu: jpu@13090000 { ++ compatible = "starfive,jpu"; ++ dma-coherent; ++ reg = <0x0 0x13090000 0x0 0x300>; ++ interrupts = <14>; ++ clocks = <&syscrg JH7110_SYSCLK_CODAJ12_AXI>, ++ <&syscrg JH7110_SYSCLK_CODAJ12_CORE>, ++ <&syscrg JH7110_SYSCLK_CODAJ12_APB>, ++ <&syscrg JH7110_SYSCLK_NOC_BUS_VDEC_AXI>, ++ <&syscrg JH7110_SYSCLK_VDEC_MAIN>, ++ <&syscrg JH7110_SYSCLK_VDEC_JPG>; ++ clock-names = "axi_clk", "core_clk", "apb_clk", ++ "noc_bus", "main_clk", "dec_clk"; ++ resets = <&syscrg JH7110_SYSRST_CODAJ12_AXI>, ++ <&syscrg JH7110_SYSRST_CODAJ12_CORE>, ++ <&syscrg JH7110_SYSRST_CODAJ12_APB>; ++ reset-names = "rst_axi", "rst_core", "rst_apb"; ++ power-domains = <&pwrc JH7110_PD_VDEC>; ++ status = "disabled"; ++ }; ++ ++ vpu_dec: vpu_dec@130a0000 { ++ compatible = "starfive,vdec"; ++ dma-coherent; ++ reg = <0x0 0x130a0000 0x0 0x10000>; ++ interrupts = <13>; ++ clocks = <&syscrg JH7110_SYSCLK_WAVE511_AXI>, ++ <&syscrg JH7110_SYSCLK_WAVE511_BPU>, ++ <&syscrg JH7110_SYSCLK_WAVE511_VCE>, ++ <&syscrg JH7110_SYSCLK_WAVE511_APB>, ++ <&syscrg JH7110_SYSCLK_NOC_BUS_VDEC_AXI>, ++ <&syscrg JH7110_SYSCLK_VDEC_MAIN>; ++ clock-names = "axi_clk", "bpu_clk", "vce_clk", ++ "apb_clk", "noc_bus", "main_clk"; ++ resets = <&syscrg JH7110_SYSRST_WAVE511_AXI>, ++ <&syscrg JH7110_SYSRST_WAVE511_BPU>, ++ <&syscrg JH7110_SYSRST_WAVE511_VCE>, ++ <&syscrg JH7110_SYSRST_WAVE511_APB>, ++ <&syscrg JH7110_SYSRST_AXIMEM0_AXI>; ++ reset-names = "rst_axi", "rst_bpu", "rst_vce", ++ "rst_apb", "rst_sram"; ++ starfive,vdec_noc_ctrl; ++ power-domains = <&pwrc JH7110_PD_VDEC>; ++ status = "disabled"; ++ }; ++ ++ vpu_enc: vpu_enc@130b0000 { ++ compatible = "starfive,venc"; ++ dma-coherent; ++ reg = <0x0 0x130b0000 0x0 0x10000>; ++ interrupts = <15>; ++ clocks = <&syscrg JH7110_SYSCLK_WAVE420L_AXI>, ++ <&syscrg JH7110_SYSCLK_WAVE420L_BPU>, ++ <&syscrg JH7110_SYSCLK_WAVE420L_VCE>, ++ <&syscrg JH7110_SYSCLK_WAVE420L_APB>, ++ <&syscrg JH7110_SYSCLK_NOC_BUS_VENC_AXI>; ++ clock-names = "axi_clk", "bpu_clk", "vce_clk", ++ "apb_clk", "noc_bus"; ++ resets = <&syscrg JH7110_SYSRST_WAVE420L_AXI>, ++ <&syscrg JH7110_SYSRST_WAVE420L_BPU>, ++ <&syscrg JH7110_SYSRST_WAVE420L_VCE>, ++ <&syscrg JH7110_SYSRST_WAVE420L_APB>, ++ <&syscrg JH7110_SYSRST_AXIMEM1_AXI>; ++ reset-names = "rst_axi", "rst_bpu", "rst_vce", ++ "rst_apb", "rst_sram"; ++ starfive,venc_noc_ctrl; ++ power-domains = <&pwrc JH7110_PD_VENC>; ++ status = "disabled"; ++ }; ++ ++ can0: can@130d0000 { ++ compatible = "starfive,jh7110-can", "ipms,can"; ++ reg = <0x0 0x130d0000 0x0 0x1000>; ++ interrupts = <112>; ++ clocks = <&syscrg JH7110_SYSCLK_CAN0_APB>, ++ <&syscrg JH7110_SYSCLK_CAN0_CAN>, ++ <&syscrg JH7110_SYSCLK_CAN0_TIMER>; ++ clock-names = "apb_clk", "core_clk", "timer_clk"; ++ resets = <&syscrg JH7110_SYSRST_CAN0_APB>, ++ <&syscrg JH7110_SYSRST_CAN0_CORE>, ++ <&syscrg JH7110_SYSRST_CAN0_TIMER>; ++ reset-names = "rst_apb", "rst_core", "rst_timer"; ++ frequency = <40000000>; ++ starfive,sys-syscon = <&sys_syscon 0x10 0x3 0x8>; ++ syscon,can_or_canfd = <0>; ++ status = "disabled"; ++ }; ++ ++ can1: can@130e0000 { ++ compatible = "starfive,jh7110-can", "ipms,can"; ++ reg = <0x0 0x130e0000 0x0 0x1000>; ++ interrupts = <113>; ++ clocks = <&syscrg JH7110_SYSCLK_CAN1_APB>, ++ <&syscrg JH7110_SYSCLK_CAN1_CAN>, ++ <&syscrg JH7110_SYSCLK_CAN1_TIMER>; ++ clock-names = "apb_clk", "core_clk", "timer_clk"; ++ resets = <&syscrg JH7110_SYSRST_CAN1_APB>, ++ <&syscrg JH7110_SYSRST_CAN1_CORE>, ++ <&syscrg JH7110_SYSRST_CAN1_TIMER>; ++ reset-names = "rst_apb", "rst_core", "rst_timer"; ++ frequency = <40000000>; ++ starfive,sys-syscon = <&sys_syscon 0x88 0x12 0x40000>; ++ syscon,can_or_canfd = <0>; ++ status = "disabled"; ++ }; ++ + crypto: crypto@16000000 { + compatible = "starfive,jh7110-crypto"; + reg = <0x0 0x16000000 0x0 0x4000>; +@@ -1119,6 +1369,42 @@ + #power-domain-cells = <1>; + }; + ++ rtc: rtc@17040000 { ++ compatible = "starfive,jh7110-rtc"; ++ reg = <0x0 0x17040000 0x0 0x10000>; ++ interrupts = <10>, <11>, <12>; ++ interrupt-names = "rtc_ms_pulse", "rtc_sec_pulse", "rtc"; ++ clocks = <&aoncrg JH7110_AONCLK_RTC_APB>, ++ <&aoncrg JH7110_AONCLK_RTC_CAL>; ++ clock-names = "pclk", "cal_clk"; ++ resets = <&aoncrg JH7110_AONRST_RTC_32K>, ++ <&aoncrg JH7110_AONRST_RTC_APB>, ++ <&aoncrg JH7110_AONRST_RTC_CAL>; ++ reset-names = "rst_osc", "rst_apb", "rst_cal"; ++ rtc,cal-clock-freq = <1000000>; ++ }; ++ ++ gpu: gpu@18000000 { ++ compatible = "img-gpu"; ++ reg = <0x0 0x18000000 0x0 0x100000>, ++ <0x0 0x130C000 0x0 0x10000>; ++ clocks = <&syscrg JH7110_SYSCLK_GPU_CORE>, ++ <&syscrg JH7110_SYSCLK_GPU_APB>, ++ <&syscrg JH7110_SYSCLK_GPU_RTC_TOGGLE>, ++ <&syscrg JH7110_SYSCLK_GPU_CORE_CLK>, ++ <&syscrg JH7110_SYSCLK_GPU_SYS_CLK>, ++ <&syscrg JH7110_SYSCLK_NOC_BUS_GPU_AXI>; ++ clock-names = "clk_bv", "clk_apb", "clk_rtc", ++ "clk_core", "clk_sys", "clk_axi"; ++ resets = <&syscrg JH7110_SYSRST_GPU_APB>, ++ <&syscrg JH7110_SYSRST_GPU_DOMA>; ++ reset-names = "rst_apb", "rst_doma"; ++ power-domains = <&pwrc JH7110_PD_GPUA>; ++ interrupts = <82>; ++ current-clock = <8000000>; ++ status = "disabled"; ++ }; ++ + csi2rx: csi-bridge@19800000 { + compatible = "starfive,jh7110-csi2rx"; + reg = <0x0 0x19800000 0x0 0x10000>; +@@ -1145,6 +1431,67 @@ + status = "disabled"; + }; + ++ vin_sysctl: vin_sysctl@19800000 { ++ compatible = "starfive,jh7110-vin"; ++ reg = <0x0 0x19800000 0x0 0x10000>, ++ <0x0 0x19810000 0x0 0x10000>, ++ <0x0 0x19820000 0x0 0x10000>, ++ <0x0 0x19840000 0x0 0x10000>, ++ <0x0 0x19870000 0x0 0x30000>, ++ <0x0 0x11840000 0x0 0x10000>, ++ <0x0 0x17030000 0x0 0x10000>, ++ <0x0 0x13020000 0x0 0x10000>; ++ reg-names = "csi2rx", "vclk", "vrst", "sctrl", ++ "isp", "trst", "pmu", "syscrg"; ++ clocks = <&ispcrg JH7110_ISPCLK_DOM4_APB_FUNC>, ++ <&ispcrg JH7110_ISPCLK_VIN_APB>, ++ <&ispcrg JH7110_ISPCLK_VIN_SYS>, ++ <&ispcrg JH7110_ISPCLK_ISPV2_TOP_WRAPPER_C>, ++ <&ispcrg JH7110_ISPCLK_DVP_INV>, ++ <&ispcrg JH7110_ISPCLK_VIN_P_AXI_WR>, ++ <&ispcrg JH7110_ISPCLK_MIPI_RX0_PXL>, ++ <&ispcrg JH7110_ISPCLK_VIN_PIXEL_IF0>, ++ <&ispcrg JH7110_ISPCLK_VIN_PIXEL_IF1>, ++ <&ispcrg JH7110_ISPCLK_VIN_PIXEL_IF2>, ++ <&ispcrg JH7110_ISPCLK_VIN_PIXEL_IF3>, ++ <&ispcrg JH7110_ISPCLK_M31DPHY_CFG_IN>, ++ <&ispcrg JH7110_ISPCLK_M31DPHY_REF_IN>, ++ <&ispcrg JH7110_ISPCLK_M31DPHY_TX_ESC_LAN0>, ++ <&syscrg JH7110_SYSCLK_ISP_TOP_CORE>, ++ <&syscrg JH7110_SYSCLK_ISP_TOP_AXI>; ++ clock-names = "clk_apb_func", "clk_pclk", "clk_sys_clk", ++ "clk_wrapper_clk_c", "clk_dvp_inv", "clk_axiwr", ++ "clk_mipi_rx0_pxl", "clk_pixel_clk_if0", ++ "clk_pixel_clk_if1", "clk_pixel_clk_if2", ++ "clk_pixel_clk_if3", "clk_m31dphy_cfgclk_in", ++ "clk_m31dphy_refclk_in", "clk_m31dphy_txclkesc_lan0", ++ "clk_ispcore_2x", "clk_isp_axi"; ++ resets = <&ispcrg JH7110_ISPRST_ISPV2_TOP_WRAPPER_P>, ++ <&ispcrg JH7110_ISPRST_ISPV2_TOP_WRAPPER_C>, ++ <&ispcrg JH7110_ISPRST_VIN_APB>, ++ <&ispcrg JH7110_ISPRST_VIN_SYS>, ++ <&ispcrg JH7110_ISPRST_VIN_P_AXI_RD>, ++ <&ispcrg JH7110_ISPRST_VIN_P_AXI_WR>, ++ <&ispcrg JH7110_ISPRST_VIN_PIXEL_IF0>, ++ <&ispcrg JH7110_ISPRST_VIN_PIXEL_IF1>, ++ <&ispcrg JH7110_ISPRST_VIN_PIXEL_IF2>, ++ <&ispcrg JH7110_ISPRST_VIN_PIXEL_IF3>, ++ <&ispcrg JH7110_ISPRST_M31DPHY_HW>, ++ <&ispcrg JH7110_ISPRST_M31DPHY_B09_AON>, ++ <&syscrg JH7110_SYSRST_ISP_TOP>, ++ <&syscrg JH7110_SYSRST_ISP_TOP_AXI>; ++ reset-names = "rst_wrapper_p", "rst_wrapper_c", "rst_pclk", ++ "rst_sys_clk", "rst_axird", "rst_axiwr", "rst_pixel_clk_if0", ++ "rst_pixel_clk_if1", "rst_pixel_clk_if2", "rst_pixel_clk_if3", ++ "rst_m31dphy_hw", "rst_m31dphy_b09_always_on", ++ "rst_isp_top_n", "rst_isp_top_axi"; ++ starfive,aon-syscon = <&aon_syscon 0x00>; ++ power-domains = <&pwrc JH7110_PD_ISP>; ++ /* irq nr: vin, isp, isp_csi, isp_scd, isp_csiline */ ++ interrupts = <92 87 88 89 90>; ++ status = "disabled"; ++ }; ++ + ispcrg: clock-controller@19810000 { + compatible = "starfive,jh7110-ispcrg"; + reg = <0x0 0x19810000 0x0 0x10000>; +@@ -1175,6 +1522,66 @@ + #phy-cells = <0>; + }; + ++ dc8200: dc8200@29400000 { ++ compatible = "starfive,jh7110-dc8200","verisilicon,dc8200"; ++ verisilicon,dss-syscon = <&dssctrl>;//20220624 panel syscon ++ reg = <0x0 0x29400000 0x0 0x100>, ++ <0x0 0x29400800 0x0 0x2000>, ++ <0x0 0x17030000 0x0 0x1000>; ++ interrupts = <95>; ++ clocks = <&syscrg JH7110_SYSCLK_NOC_BUS_DISP_AXI>, ++ <&syscrg JH7110_SYSCLK_VOUT_SRC>, ++ <&syscrg JH7110_SYSCLK_VOUT_TOP_AXI>, ++ <&syscrg JH7110_SYSCLK_VOUT_TOP_AHB>, ++ <&voutcrg JH7110_VOUTCLK_DC8200_PIX0>, ++ <&voutcrg JH7110_VOUTCLK_DC8200_PIX1>, ++ <&voutcrg JH7110_VOUTCLK_DC8200_AXI>, ++ <&voutcrg JH7110_VOUTCLK_DC8200_CORE>, ++ <&voutcrg JH7110_VOUTCLK_DC8200_AHB>, ++ <&syscrg JH7110_SYSCLK_VOUT_TOP_AXI>, ++ <&voutcrg JH7110_VOUTCLK_DOM_VOUT_TOP_LCD>, ++ <&hdmitx0_pixelclk>, ++ <&voutcrg JH7110_VOUTCLK_DC8200_PIX>; ++ clock-names = "noc_disp","vout_src", ++ "top_vout_axi","top_vout_ahb", ++ "pix_clk","vout_pix1", ++ "axi_clk","core_clk","vout_ahb", ++ "vout_top_axi","vout_top_lcd","hdmitx0_pixelclk","dc8200_pix0"; ++ resets = <&syscrg JH7110_SYSRST_VOUT_TOP_SRC>, ++ <&voutcrg JH7110_VOUTRST_DC8200_AXI>, ++ <&voutcrg JH7110_VOUTRST_DC8200_AHB>, ++ <&voutcrg JH7110_VOUTRST_DC8200_CORE>, ++ <&syscrg JH7110_SYSRST_NOC_BUS_DISP_AXI>; ++ reset-names = "rst_vout_src","rst_axi","rst_ahb","rst_core", ++ "rst_noc_disp"; ++ status = "disabled"; ++ }; ++ ++ hdmi: hdmi@29590000 { ++ compatible = "starfive,jh7110-hdmi","inno,hdmi"; ++ reg = <0x0 0x29590000 0x0 0x4000>; ++ interrupts = <99>; ++ /*interrupts = ;*/ ++ /*clocks = <&cru PCLK_HDMI>;*/ ++ /*clock-names = "pclk";*/ ++ /*pinctrl-names = "default";*/ ++ /*pinctrl-0 = <&hdmi_ctl>;*/ ++ clocks = <&voutcrg JH7110_VOUTCLK_HDMI_TX_SYS>, ++ <&voutcrg JH7110_VOUTCLK_HDMI_TX_MCLK>, ++ <&voutcrg JH7110_VOUTCLK_HDMI_TX_BCLK>, ++ <&hdmitx0_pixelclk>; ++ clock-names = "sysclk", "mclk","bclk","pclk"; ++ resets = <&voutcrg JH7110_VOUTRST_HDMI_TX_HDMI>; ++ reset-names = "hdmi_tx"; ++ #sound-dai-cells = <0>; ++ status = "disabled"; ++ }; ++ ++ dssctrl: dssctrl@295B0000 { ++ compatible = "starfive,jh7110-dssctrl","verisilicon,dss-ctrl", "syscon"; ++ reg = <0 0x295B0000 0 0x90>; ++ }; ++ + voutcrg: clock-controller@295c0000 { + compatible = "starfive,jh7110-voutcrg"; + reg = <0x0 0x295c0000 0x0 0x10000>; +@@ -1193,6 +1600,67 @@ + power-domains = <&pwrc JH7110_PD_VOUT>; + }; + ++ mipi_dsi: mipi@295d0000 { ++ compatible = "starfive,jh7110-mipi_dsi","cdns,dsi"; ++ reg = <0x0 0x295d0000 0x0 0x10000>; ++ interrupts = <98>; ++ reg-names = "dsi"; ++ clocks = <&voutcrg JH7110_VOUTCLK_DSITX_SYS>, ++ <&voutcrg JH7110_VOUTCLK_DSITX_APB>, ++ <&voutcrg JH7110_VOUTCLK_DSITX_TXESC>, ++ <&voutcrg JH7110_VOUTCLK_DSITX_DPI>; ++ clock-names = "dpi", "apb", "txesc", "sys"; ++ resets = <&voutcrg JH7110_VOUTRST_DSITX_DPI>, ++ <&voutcrg JH7110_VOUTRST_DSITX_APB>, ++ <&voutcrg JH7110_VOUTRST_DSITX_RXESC>, ++ <&voutcrg JH7110_VOUTRST_DSITX_SYS>, ++ <&voutcrg JH7110_VOUTRST_DSITX_TXBYTEHS>, ++ <&voutcrg JH7110_VOUTRST_DSITX_TXESC>; ++ reset-names = "dsi_dpi", "dsi_apb", "dsi_rxesc", ++ "dsi_sys", "dsi_txbytehs", "dsi_txesc"; ++ phys = <&mipi_dphy>; ++ phy-names = "dphy"; ++ status = "disabled"; ++ }; ++ ++ mipi_dphy: mipi-dphy@295e0000{ ++ compatible = "starfive,jh7110-mipi-dphy-tx","m31,mipi-dphy-tx"; ++ reg = <0x0 0x295e0000 0x0 0x10000>; ++ clocks = <&voutcrg JH7110_VOUTCLK_MIPITX_DPHY_TXESC>; ++ clock-names = "dphy_txesc"; ++ resets = <&voutcrg JH7110_VOUTRST_MIPITX_DPHY_SYS>, ++ <&voutcrg JH7110_VOUTRST_MIPITX_DPHY_TXBYTEHS>; ++ reset-names = "dphy_sys", "dphy_txbytehs"; ++ #phy-cells = <0>; ++ status = "disabled"; ++ }; ++ ++ co_process: e24@6e210000 { ++ compatible = "starfive,e24"; ++ dma-coherent; ++ reg = <0x0 0x6e210000 0x0 0x00001000>, ++ <0x0 0x6e211000 0x0 0x0003f000>; ++ reg-names = "ecmd", "espace"; ++ clocks = <&stgcrg JH7110_STGCLK_E2_RTC>, ++ <&stgcrg JH7110_STGCLK_E2_CORE>, ++ <&stgcrg JH7110_STGCLK_E2_DBG>; ++ clock-names = "clk_rtc", "clk_core", "clk_dbg"; ++ resets = <&stgcrg JH7110_STGRST_E24_CORE>; ++ reset-names = "e24_core"; ++ starfive,stg-syscon = <&stg_syscon>; ++ interrupt-parent = <&plic>; ++ firmware-name = "e24_elf"; ++ irq-mode = <1>; ++ mbox-names = "tx", "rx"; ++ mboxes = <&mailbox_contrl0 0 2>, ++ <&mailbox_contrl0 2 0>; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges = <0x6ce00000 0x0 0x6ce00000 0x1600000>; ++ status = "disabled"; ++ dsp@0 {}; ++ }; ++ + pcie0: pcie@940000000 { + compatible = "starfive,jh7110-pcie"; + reg = <0x9 0x40000000 0x0 0x1000000>, diff --git a/target/linux/starfive/patches-6.6/0057-riscv-dts-starfive-Add-JH7110-EVB-expanded-device-tr.patch b/target/linux/starfive/patches-6.6/0057-riscv-dts-starfive-Add-JH7110-EVB-expanded-device-tr.patch new file mode 100644 index 0000000000..7a23bf931b --- /dev/null +++ b/target/linux/starfive/patches-6.6/0057-riscv-dts-starfive-Add-JH7110-EVB-expanded-device-tr.patch @@ -0,0 +1,728 @@ +From cae7550054ca0cd940bbc1501ae5611f5d2957e6 Mon Sep 17 00:00:00 2001 +From: Hal Feng +Date: Wed, 20 Sep 2023 14:53:22 +0800 +Subject: [PATCH 057/116] riscv: dts: starfive: Add JH7110 EVB expanded device + tree + +Add JH7110 EVB expanded device tree. +The code is ported from tag JH7110_SDK_6.1_v5.11.3 + +Signed-off-by: Hal Feng +--- + arch/riscv/boot/dts/starfive/Makefile | 11 +- + .../starfive/jh7110-evb-can-pdm-pwmdac.dts | 102 ++++++++++++++++ + .../dts/starfive/jh7110-evb-dvp-rgb2hdmi.dts | 37 ++++++ + .../dts/starfive/jh7110-evb-i2s-ac108.dts | 72 ++++++++++++ + .../dts/starfive/jh7110-evb-pcie-i2s-sd.dts | 111 ++++++++++++++++++ + .../dts/starfive/jh7110-evb-spi-uart2.dts | 65 ++++++++++ + .../starfive/jh7110-evb-uart1-rgb2hdmi.dts | 57 +++++++++ + .../starfive/jh7110-evb-uart4-emmc-spdif.dts | 78 ++++++++++++ + .../starfive/jh7110-evb-uart5-pwm-i2c-tdm.dts | 95 +++++++++++++++ + .../dts/starfive/jh7110-evb-usbdevice.dts | 35 ++++++ + 10 files changed, 662 insertions(+), 1 deletion(-) + create mode 100644 arch/riscv/boot/dts/starfive/jh7110-evb-can-pdm-pwmdac.dts + create mode 100644 arch/riscv/boot/dts/starfive/jh7110-evb-dvp-rgb2hdmi.dts + create mode 100644 arch/riscv/boot/dts/starfive/jh7110-evb-i2s-ac108.dts + create mode 100644 arch/riscv/boot/dts/starfive/jh7110-evb-pcie-i2s-sd.dts + create mode 100644 arch/riscv/boot/dts/starfive/jh7110-evb-spi-uart2.dts + create mode 100644 arch/riscv/boot/dts/starfive/jh7110-evb-uart1-rgb2hdmi.dts + create mode 100644 arch/riscv/boot/dts/starfive/jh7110-evb-uart4-emmc-spdif.dts + create mode 100644 arch/riscv/boot/dts/starfive/jh7110-evb-uart5-pwm-i2c-tdm.dts + create mode 100644 arch/riscv/boot/dts/starfive/jh7110-evb-usbdevice.dts + +--- a/arch/riscv/boot/dts/starfive/Makefile ++++ b/arch/riscv/boot/dts/starfive/Makefile +@@ -12,4 +12,13 @@ dtb-$(CONFIG_ARCH_STARFIVE) += jh7100-st + dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-starfive-visionfive-2-v1.2a.dtb + dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-starfive-visionfive-2-v1.3b.dtb + +-dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-evb.dtb ++dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-evb.dtb \ ++ jh7110-evb-pcie-i2s-sd.dtb \ ++ jh7110-evb-spi-uart2.dtb \ ++ jh7110-evb-uart4-emmc-spdif.dtb \ ++ jh7110-evb-uart5-pwm-i2c-tdm.dtb \ ++ jh7110-evb-dvp-rgb2hdmi.dtb \ ++ jh7110-evb-can-pdm-pwmdac.dtb \ ++ jh7110-evb-i2s-ac108.dtb \ ++ jh7110-evb-usbdevice.dtb \ ++ jh7110-evb-uart1-rgb2hdmi.dtb +--- /dev/null ++++ b/arch/riscv/boot/dts/starfive/jh7110-evb-can-pdm-pwmdac.dts +@@ -0,0 +1,102 @@ ++// SPDX-License-Identifier: GPL-2.0 OR MIT ++/* ++ * Copyright (C) 2022 StarFive Technology Co., Ltd. ++ */ ++ ++/dts-v1/; ++#include "jh7110-evb.dtsi" ++ ++/ { ++ model = "StarFive JH7110 EVB"; ++ compatible = "starfive,jh7110-evb", "starfive,jh7110"; ++ ++ sound2: snd-card2 { ++ compatible = "simple-audio-card"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ simple-audio-card,name = "StarFive-PDM-Sound-Card"; ++ simple-audio-card,dai-link@0 { ++ reg = <0>; ++ format = "i2s"; ++ bitclock-master = <&dailink_master>; ++ frame-master = <&dailink_master>; ++ ++ dailink_master:cpu { ++ sound-dai = <&i2srx_mst>; ++ }; ++ ++ dailink_slave:codec { ++ sound-dai = <&pdm>; ++ }; ++ }; ++ }; ++ ++ pwmdac_codec: pwmdac-codec { ++ compatible = "linux,spdif-dit"; ++ #sound-dai-cells = <0>; ++ }; ++ ++ sound3: snd-card3 { ++ compatible = "simple-audio-card"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ simple-audio-card,name = "StarFive-PWMDAC-Sound-Card"; ++ simple-audio-card,dai-link@0 { ++ reg = <0>; ++ format = "left_j"; ++ bitclock-master = <&sndcpu0>; ++ frame-master = <&sndcpu0>; ++ ++ sndcpu0: cpu { ++ sound-dai = <&pwmdac>; ++ }; ++ ++ codec { ++ sound-dai = <&pwmdac_codec>; ++ }; ++ }; ++ }; ++}; ++ ++&mmc0 { ++ assigned-clocks = <&syscrg JH7110_SYSCLK_SDIO0_SDCARD>; ++ assigned-clock-rates = <50000000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&sdcard0_pins>; ++ max-frequency = <100000000>; ++ card-detect-delay = <300>; ++ bus-width = <4>; ++ broken-cd; ++ post-power-on-delay-ms = <200>; ++ status = "okay"; ++}; ++ ++&usb0 { ++ status = "okay"; ++}; ++ ++&pcie1 { ++ status = "okay"; ++}; ++ ++&can0 { ++ status = "okay"; ++}; ++ ++&can1 { ++ status = "okay"; ++}; ++ ++&i2srx_mst { ++ status = "okay"; ++}; ++ ++&pwmdac { ++ status = "okay"; ++}; ++ ++&pdm { ++ status = "okay"; ++}; +--- /dev/null ++++ b/arch/riscv/boot/dts/starfive/jh7110-evb-dvp-rgb2hdmi.dts +@@ -0,0 +1,37 @@ ++// SPDX-License-Identifier: GPL-2.0 OR MIT ++/* ++ * Copyright (C) 2022 StarFive Technology Co., Ltd. ++ */ ++ ++/dts-v1/; ++#include "jh7110-evb.dtsi" ++ ++/ { ++ model = "StarFive JH7110 EVB"; ++ compatible = "starfive,jh7110-evb", "starfive,jh7110"; ++}; ++ ++&vin_sysctl { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&dvp_pins>; ++}; ++ ++&rgb_output { ++ status = "okay"; ++}; ++ ++&tda988x_pin { ++ status = "okay"; ++}; ++ ++&dsi_output { ++ status = "disabled"; ++}; ++ ++&mipi_dsi { ++ status = "disabled"; ++}; ++ ++&mipi_dphy { ++ status = "disabled"; ++}; +--- /dev/null ++++ b/arch/riscv/boot/dts/starfive/jh7110-evb-i2s-ac108.dts +@@ -0,0 +1,72 @@ ++// SPDX-License-Identifier: GPL-2.0 OR MIT ++/* ++ * Copyright (C) 2022 StarFive Technology Co., Ltd. ++ */ ++ ++/dts-v1/; ++#include "jh7110-evb.dtsi" ++ ++/ { ++ model = "StarFive JH7110 EVB"; ++ compatible = "starfive,jh7110-evb", "starfive,jh7110"; ++ ++ /* i2s + ac108 */ ++ sound0: snd-card0 { ++ compatible = "simple-audio-card"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ simple-audio-card,name = "StarFive-AC108-Sound-Card"; ++ simple-audio-card,dai-link@0 { ++ reg = <0>; ++ format = "i2s"; ++ bitclock-master = <&sndcodec1>; ++ frame-master = <&sndcodec1>; ++ ++ widgets = "Microphone", "Mic Jack", ++ "Line", "Line In", ++ "Line", "Line Out", ++ "Speaker", "Speaker", ++ "Headphone", "Headphone Jack"; ++ routing = "Headphone Jack", "HP_L", ++ "Headphone Jack", "HP_R", ++ "Speaker", "SPK_LP", ++ "Speaker", "SPK_LN", ++ "LINPUT1", "Mic Jack", ++ "LINPUT3", "Mic Jack", ++ "RINPUT1", "Mic Jack", ++ "RINPUT2", "Mic Jack"; ++ ++ cpu { ++ sound-dai = <&i2srx>; ++ }; ++ ++ sndcodec1: codec { ++ sound-dai = <&ac108>; ++ clocks = <&ac108_mclk>; ++ clock-names = "mclk"; ++ }; ++ }; ++ }; ++}; ++ ++&mmc0 { ++ assigned-clocks = <&syscrg JH7110_SYSCLK_SDIO0_SDCARD>; ++ assigned-clock-rates = <50000000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&sdcard0_pins>; ++ max-frequency = <100000000>; ++ card-detect-delay = <300>; ++ bus-width = <4>; ++ broken-cd; ++ post-power-on-delay-ms = <200>; ++ status = "okay"; ++}; ++ ++&i2c0 { ++ status = "okay"; ++}; ++ ++&i2srx { ++ status = "okay"; ++}; +--- /dev/null ++++ b/arch/riscv/boot/dts/starfive/jh7110-evb-pcie-i2s-sd.dts +@@ -0,0 +1,111 @@ ++// SPDX-License-Identifier: GPL-2.0 OR MIT ++/* ++ * Copyright (C) 2022 StarFive Technology Co., Ltd. ++ */ ++ ++/dts-v1/; ++#include "jh7110-evb.dtsi" ++ ++/ { ++ model = "StarFive JH7110 EVB"; ++ compatible = "starfive,jh7110-evb", "starfive,jh7110"; ++ ++ /* i2s + wm8960 */ ++ sound6: snd-card6 { ++ compatible = "simple-audio-card"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ simple-audio-card,name = "StarFive-WM8960-Sound-Card"; ++ simple-audio-card,dai-link@0 { ++ reg = <0>; ++ status = "okay"; ++ format = "i2s"; ++ bitclock-master = <&sndcodec1>; ++ frame-master = <&sndcodec1>; ++ ++ widgets = "Microphone", "Mic Jack", ++ "Line", "Line In", ++ "Line", "Line Out", ++ "Speaker", "Speaker", ++ "Headphone", "Headphone Jack"; ++ routing = "Headphone Jack", "HP_L", ++ "Headphone Jack", "HP_R", ++ "Speaker", "SPK_LP", ++ "Speaker", "SPK_LN", ++ "LINPUT1", "Mic Jack", ++ "LINPUT3", "Mic Jack", ++ "RINPUT1", "Mic Jack", ++ "RINPUT2", "Mic Jack"; ++ cpu0 { ++ sound-dai = <&i2srx>; ++ }; ++ cpu1 { ++ sound-dai = <&i2stx1>; ++ }; ++ ++ sndcodec1:codec { ++ sound-dai = <&wm8960>; ++ clocks = <&wm8960_mclk>; ++ clock-names = "mclk"; ++ }; ++ }; ++ }; ++}; ++ ++&mmc0 { ++ assigned-clocks = <&syscrg JH7110_SYSCLK_SDIO0_SDCARD>; ++ assigned-clock-rates = <50000000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&sdcard0_pins>; ++ max-frequency = <100000000>; ++ card-detect-delay = <300>; ++ bus-width = <4>; ++ broken-cd; ++ post-power-on-delay-ms = <200>; ++ status = "okay"; ++}; ++ ++&pcie1 { ++ status = "okay"; ++}; ++ ++&pcie0 { ++ status = "okay"; ++}; ++ ++&uart3 { ++ status = "okay"; ++}; ++ ++&i2c0 { ++ status = "okay"; ++}; ++ ++&usb0 { ++ clocks = <&stgcrg JH7110_STGCLK_USB0_LPM>, ++ <&stgcrg JH7110_STGCLK_USB0_STB>, ++ <&stgcrg JH7110_STGCLK_USB0_APB>, ++ <&stgcrg JH7110_STGCLK_USB0_AXI>, ++ <&stgcrg JH7110_STGCLK_USB0_UTMI_APB>; ++ clock-names = "lpm", "stb", "apb", "axi", "utmi_apb"; ++ resets = <&stgcrg JH7110_STGRST_USB0_PWRUP>, ++ <&stgcrg JH7110_STGRST_USB0_APB>, ++ <&stgcrg JH7110_STGRST_USB0_AXI>, ++ <&stgcrg JH7110_STGRST_USB0_UTMI_APB>; ++ reset-names = "pwrup", "apb", "axi", "utmi_apb"; ++ dr_mode = "host"; /*host or peripheral*/ ++ starfive,usb2-only; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&usb_pins>; ++ status = "okay"; ++}; ++ ++&i2srx { ++ status = "okay"; ++}; ++ ++&i2stx1 { ++ status = "okay"; ++}; ++ +--- /dev/null ++++ b/arch/riscv/boot/dts/starfive/jh7110-evb-spi-uart2.dts +@@ -0,0 +1,65 @@ ++// SPDX-License-Identifier: GPL-2.0 OR MIT ++/* ++ * Copyright (C) 2022 StarFive Technology Co., Ltd. ++ */ ++ ++/dts-v1/; ++#include "jh7110-evb.dtsi" ++ ++/ { ++ model = "StarFive JH7110 EVB"; ++ compatible = "starfive,jh7110-evb", "starfive,jh7110"; ++}; ++ ++&mmc0 { ++ assigned-clocks = <&syscrg JH7110_SYSCLK_SDIO0_SDCARD>; ++ assigned-clock-rates = <50000000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&sdcard0_pins>; ++ max-frequency = <100000000>; ++ card-detect-delay = <300>; ++ bus-width = <4>; ++ broken-cd; ++ post-power-on-delay-ms = <200>; ++ status = "okay"; ++}; ++ ++&usb0 { ++ status = "okay"; ++}; ++ ++&pcie1 { ++ status = "okay"; ++}; ++ ++&uart2 { ++ status = "okay"; ++}; ++ ++&spi0 { ++ status = "okay"; ++}; ++ ++&spi1 { ++ status = "okay"; ++}; ++ ++&spi2 { ++ status = "okay"; ++}; ++ ++&spi3 { ++ status = "okay"; ++}; ++ ++&spi4 { ++ status = "okay"; ++}; ++ ++&spi5 { ++ status = "okay"; ++}; ++ ++&spi6 { ++ status = "okay"; ++}; +--- /dev/null ++++ b/arch/riscv/boot/dts/starfive/jh7110-evb-uart1-rgb2hdmi.dts +@@ -0,0 +1,57 @@ ++// SPDX-License-Identifier: GPL-2.0 OR MIT ++/* ++ * Copyright (C) 2022 StarFive Technology Co., Ltd. ++ */ ++ ++/dts-v1/; ++#include "jh7110-evb.dtsi" ++ ++/ { ++ model = "StarFive JH7110 EVB"; ++ compatible = "starfive,jh7110-evb", "starfive,jh7110"; ++}; ++ ++&mmc0 { ++ assigned-clocks = <&syscrg JH7110_SYSCLK_SDIO0_SDCARD>; ++ assigned-clock-rates = <50000000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&sdcard0_pins>; ++ max-frequency = <100000000>; ++ card-detect-delay = <300>; ++ bus-width = <4>; ++ broken-cd; ++ post-power-on-delay-ms = <200>; ++ status = "okay"; ++}; ++ ++&usb0 { ++ status = "okay"; ++}; ++ ++&pcie1 { ++ status = "okay"; ++}; ++ ++&uart1 { ++ status = "okay"; ++}; ++ ++&rgb_output { ++ status = "okay"; ++}; ++ ++&tda988x_pin { ++ status = "okay"; ++}; ++ ++&dsi_output { ++ status = "disabled"; ++}; ++ ++&mipi_dsi { ++ status = "disabled"; ++}; ++ ++&mipi_dphy { ++ status = "disabled"; ++}; +--- /dev/null ++++ b/arch/riscv/boot/dts/starfive/jh7110-evb-uart4-emmc-spdif.dts +@@ -0,0 +1,78 @@ ++// SPDX-License-Identifier: GPL-2.0 OR MIT ++/* ++ * Copyright (C) 2022 StarFive Technology Co., Ltd. ++ */ ++ ++/dts-v1/; ++#include "jh7110-evb.dtsi" ++ ++/ { ++ model = "StarFive JH7110 EVB"; ++ compatible = "starfive,jh7110-evb", "starfive,jh7110"; ++ ++ spdif_transmitter: spdif_transmitter { ++ compatible = "linux,spdif-dit"; ++ #sound-dai-cells = <0>; ++ }; ++ ++ sound4: snd-card4 { ++ compatible = "simple-audio-card"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ simple-audio-card,name = "StarFive-SPDIF-Sound-Card"; ++ simple-audio-card,dai-link@0 { ++ reg = <0>; ++ format = "left_j"; ++ bitclock-master = <&sndcpu0>; ++ frame-master = <&sndcpu0>; ++ ++ sndcpu0: cpu { ++ sound-dai = <&spdif>; ++ }; ++ ++ codec { ++ sound-dai = <&spdif_transmitter>; ++ }; ++ }; ++ }; ++}; ++ ++&usb0 { ++ status = "okay"; ++}; ++ ++&pcie1 { ++ status = "okay"; ++}; ++ ++&uart4 { ++ status = "okay"; ++}; ++ ++&mmc0 { ++ assigned-clocks = <&syscrg JH7110_SYSCLK_SDIO0_SDCARD>; ++ assigned-clock-rates = <50000000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&emmc0_pins>; ++ max-frequency = <100000000>; ++ card-detect-delay = <300>; ++ bus-width = <8>; ++ cap-mmc-highspeed; ++ mmc-hs200-1_8v; ++ non-removable; ++ cap-mmc-hw-reset; ++ board-is-evb; ++ post-power-on-delay-ms = <200>; ++ status = "okay"; ++}; ++ ++&pwm { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pwm_ch6to7_pins>; ++ status = "okay"; ++}; ++ ++&spdif { ++ status = "okay"; ++}; +--- /dev/null ++++ b/arch/riscv/boot/dts/starfive/jh7110-evb-uart5-pwm-i2c-tdm.dts +@@ -0,0 +1,95 @@ ++// SPDX-License-Identifier: GPL-2.0 OR MIT ++/* ++ * Copyright (C) 2022 StarFive Technology Co., Ltd. ++ */ ++ ++/dts-v1/; ++#include "jh7110-evb.dtsi" ++ ++/ { ++ model = "StarFive JH7110 EVB"; ++ compatible = "starfive,jh7110-evb", "starfive,jh7110"; ++ ++ sound5: snd-card5 { ++ compatible = "simple-audio-card"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ simple-audio-card,name = "StarFive-TDM-Sound-Card"; ++ simple-audio-card,widgets = "Microphone", "Mic Jack", ++ "Line", "Line In", ++ "Line", "Line Out", ++ "Speaker", "Speaker", ++ "Headphone", "Headphone Jack"; ++ simple-audio-card,routing = "Headphone Jack", "HP_L", ++ "Headphone Jack", "HP_R", ++ "Speaker", "SPK_LP", ++ "Speaker", "SPK_LN", ++ "LINPUT1", "Mic Jack", ++ "LINPUT3", "Mic Jack", ++ "RINPUT1", "Mic Jack", ++ "RINPUT2", "Mic Jack"; ++ ++ simple-audio-card,dai-link@0 { ++ reg = <0>; ++ format = "dsp_a"; ++ bitclock-master = <&dailink_master>; ++ frame-master = <&dailink_master>; ++ ++ cpu { ++ sound-dai = <&tdm>; ++ }; ++ dailink_master: codec { ++ sound-dai = <&wm8960>; ++ clocks = <&wm8960_mclk>; ++ }; ++ }; ++ }; ++}; ++ ++&mmc0 { ++ assigned-clocks = <&syscrg JH7110_SYSCLK_SDIO0_SDCARD>; ++ assigned-clock-rates = <50000000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&sdcard0_pins>; ++ max-frequency = <100000000>; ++ card-detect-delay = <300>; ++ bus-width = <4>; ++ broken-cd; ++ post-power-on-delay-ms = <200>; ++ status = "okay"; ++}; ++ ++&usb0 { ++ status = "okay"; ++}; ++ ++&pcie1 { ++ status = "okay"; ++}; ++ ++&uart5 { ++ status = "okay"; ++}; ++ ++&pwm { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pwm_ch0to3_pins &pwm_ch4to5_pins>; ++ status = "okay"; ++}; ++ ++&tdm { ++ status = "okay"; ++}; ++ ++&i2c0 { ++ status = "okay"; ++}; ++ ++&i2c1 { ++ status = "okay"; ++}; ++ ++&i2c3 { ++ status = "okay"; ++}; +--- /dev/null ++++ b/arch/riscv/boot/dts/starfive/jh7110-evb-usbdevice.dts +@@ -0,0 +1,35 @@ ++// SPDX-License-Identifier: GPL-2.0 OR MIT ++/* ++ * Copyright (C) 2022 StarFive Technology Co., Ltd. ++ */ ++ ++/dts-v1/; ++#include "jh7110-evb.dtsi" ++ ++/ { ++ model = "StarFive JH7110 EVB"; ++ compatible = "starfive,jh7110-evb", "starfive,jh7110"; ++}; ++ ++/* default sd card */ ++&mmc0 { ++ assigned-clocks = <&syscrg JH7110_SYSCLK_SDIO0_SDCARD>; ++ assigned-clock-rates = <50000000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&sdcard0_pins>; ++ max-frequency = <100000000>; ++ card-detect-delay = <300>; ++ bus-width = <4>; ++ broken-cd; ++ post-power-on-delay-ms = <200>; ++ status = "okay"; ++}; ++ ++&usb0 { ++ dr_mode = "peripheral"; /*host or peripheral*/ ++ status = "okay"; ++}; ++ ++&pcie1 { ++ status = "okay"; ++}; diff --git a/target/linux/starfive/patches-6.6/0058-riscv-dts-starfive-Add-evb-overlay-dtso-subdir.patch b/target/linux/starfive/patches-6.6/0058-riscv-dts-starfive-Add-evb-overlay-dtso-subdir.patch new file mode 100644 index 0000000000..a75ffc2aff --- /dev/null +++ b/target/linux/starfive/patches-6.6/0058-riscv-dts-starfive-Add-evb-overlay-dtso-subdir.patch @@ -0,0 +1,485 @@ +From e9122ceaf2d8767753e2a126c14b29b78280446d Mon Sep 17 00:00:00 2001 +From: Hal Feng +Date: Tue, 19 Sep 2023 21:35:39 +0800 +Subject: [PATCH 058/116] riscv: dts: starfive: Add evb-overlay dtso subdir + +Create subdir evb-overlay/ and add overlay .dtso for JH7110 EVB. +The code is ported from tag JH7110_SDK_6.1_v5.11.3 + +Signed-off-by: Hal Feng +--- + arch/riscv/boot/dts/starfive/Makefile | 1 + + .../boot/dts/starfive/evb-overlay/Makefile | 7 + + .../evb-overlay/jh7110-evb-overlay-can.dtso | 24 ++++ + .../jh7110-evb-overlay-rgb2hdmi.dtso | 24 ++++ + .../evb-overlay/jh7110-evb-overlay-sdio.dtso | 78 +++++++++++ + .../evb-overlay/jh7110-evb-overlay-spi.dtso | 72 ++++++++++ + .../jh7110-evb-overlay-uart4-emmc.dtso | 130 ++++++++++++++++++ + .../jh7110-evb-overlay-uart5-pwm.dtso | 92 +++++++++++++ + 8 files changed, 428 insertions(+) + create mode 100644 arch/riscv/boot/dts/starfive/evb-overlay/Makefile + create mode 100644 arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-can.dtso + create mode 100644 arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-rgb2hdmi.dtso + create mode 100644 arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-sdio.dtso + create mode 100644 arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-spi.dtso + create mode 100644 arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-uart4-emmc.dtso + create mode 100644 arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-uart5-pwm.dtso + +--- a/arch/riscv/boot/dts/starfive/Makefile ++++ b/arch/riscv/boot/dts/starfive/Makefile +@@ -12,6 +12,7 @@ dtb-$(CONFIG_ARCH_STARFIVE) += jh7100-st + dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-starfive-visionfive-2-v1.2a.dtb + dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-starfive-visionfive-2-v1.3b.dtb + ++subdir-y += evb-overlay + dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-evb.dtb \ + jh7110-evb-pcie-i2s-sd.dtb \ + jh7110-evb-spi-uart2.dtb \ +--- /dev/null ++++ b/arch/riscv/boot/dts/starfive/evb-overlay/Makefile +@@ -0,0 +1,7 @@ ++# SPDX-License-Identifier: GPL-2.0 ++dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-evb-overlay-can.dtbo \ ++ jh7110-evb-overlay-sdio.dtbo \ ++ jh7110-evb-overlay-spi.dtbo \ ++ jh7110-evb-overlay-uart4-emmc.dtbo \ ++ jh7110-evb-overlay-uart5-pwm.dtbo \ ++ jh7110-evb-overlay-rgb2hdmi.dtbo +--- /dev/null ++++ b/arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-can.dtso +@@ -0,0 +1,24 @@ ++/dts-v1/; ++/plugin/; ++#include ++#include "../jh7110-pinfunc.h" ++/ { ++ compatible = "starfive,jh7110"; ++ ++ //can0 ++ fragment@0 { ++ target-path = "/soc/can@130d0000"; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ //can1 ++ fragment@1 { ++ target-path = "/soc/can@130e0000"; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++}; ++ +--- /dev/null ++++ b/arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-rgb2hdmi.dtso +@@ -0,0 +1,24 @@ ++/dts-v1/; ++/plugin/; ++#include ++#include "../jh7110-pinfunc.h" ++/ { ++ compatible = "starfive,jh7110"; ++ ++ //hdmi_output ++ fragment@0 { ++ target-path = "/tda988x_pin"; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ //uart1 ++ fragment@1 { ++ target-path = "/soc/serial@10010000"; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++}; ++ +--- /dev/null ++++ b/arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-sdio.dtso +@@ -0,0 +1,78 @@ ++/dts-v1/; ++/plugin/; ++#include ++#include "../jh7110-pinfunc.h" ++/ { ++ compatible = "starfive,jh7110"; ++ ++ //sysgpio ++ fragment@0 { ++ target-path = "/soc/pinctrl@13040000"; ++ __overlay__ { ++ dt_sdcard1_pins: dt-sdcard1-0 { ++ sdcard-pins { ++ pinmux = , ++ , ++ , ++ , ++ , ++ ; ++ bias-pull-up; ++ input-enable; ++ }; ++ }; ++ }; ++ }; ++ ++ //uart3 ++ fragment@1 { ++ target-path = "/soc/serial@12000000"; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ //i2c0 ++ fragment@2 { ++ target-path = "/soc/i2c@10030000"; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ //mmc1 ++ fragment@3 { ++ target-path = "/soc/mmc@16020000"; ++ __overlay__ { ++ max-frequency = <100000000>; ++ card-detect-delay = <300>; ++ bus-width = <4>; ++ no-sdio; ++ no-mmc; ++ broken-cd; ++ sd-uhs-sdr12; ++ sd-uhs-sdr25; ++ sd-uhs-sdr50; ++ sd-uhs-sdr104; ++ sd-uhs-ddr50; ++ cap-sd-highspeed; ++ post-power-on-delay-ms = <200>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&dt_sdcard1_pins>; ++ status = "okay"; ++ }; ++ }; ++}; ++ +--- /dev/null ++++ b/arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-spi.dtso +@@ -0,0 +1,72 @@ ++/dts-v1/; ++/plugin/; ++#include ++#include "../jh7110-pinfunc.h" ++/ { ++ compatible = "starfive,jh7110"; ++ ++ //spi0 ++ fragment@0 { ++ target-path = "/soc/spi@10060000"; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ //spi1 ++ fragment@1 { ++ target-path = "/soc/spi@10070000"; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ //spi2 ++ fragment@2 { ++ target-path = "/soc/spi@10080000"; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ //spi3 ++ fragment@3 { ++ target-path = "/soc/spi@12070000"; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ //spi4 ++ fragment@4 { ++ target-path = "/soc/spi@12080000"; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ //spi5 ++ fragment@5 { ++ target-path = "/soc/spi@12090000"; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ //spi6 ++ fragment@6 { ++ target-path = "/soc/spi@120a0000"; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ //uart2 ++ fragment@7 { ++ target-path = "/soc/serial@10020000"; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++}; ++ +--- /dev/null ++++ b/arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-uart4-emmc.dtso +@@ -0,0 +1,130 @@ ++/dts-v1/; ++/plugin/; ++#include ++#include "../jh7110-pinfunc.h" ++/ { ++ compatible = "starfive,jh7110"; ++ ++ //sysgpio ++ fragment@0 { ++ target-path = "/soc/pinctrl@13040000"; ++ __overlay__ { ++ dt_emmc0_pins: dt-emmc0-0 { ++ emmc-pins { ++ pinmux = , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ ; ++ bias-pull-up; ++ drive-strength = <12>; ++ input-enable; ++ slew-rate = <1>; ++ }; ++ }; ++ ++ dt_emmc1_pins: dt-emmc1-0 { ++ emmc-pins { ++ pinmux = , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ ; ++ bias-pull-up; ++ input-enable; ++ }; ++ }; ++ }; ++ }; ++ ++ //aongpio ++ fragment@1 { ++ target-path = "/soc/pinctrl@17020000"; ++ __overlay__ { ++ dt_pwm_ch6to7_pins: dt-pwm-ch6to7-0 { ++ pwm-pins { ++ pinmux = , ++ ; ++ drive-strength = <12>; ++ }; ++ }; ++ }; ++ }; ++ ++ //uart4 ++ fragment@2 { ++ target-path = "/soc/serial@12010000"; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ //mmc1 ++ fragment@3 { ++ target-path = "/soc/mmc@16020000"; ++ __overlay__ { ++ clock-frequency = <102400000>; ++ max-frequency = <100000000>; ++ card-detect-delay = <300>; ++ bus-width = <8>; ++ cap-mmc-hw-reset; ++ non-removable; ++ cap-mmc-highspeed; ++ post-power-on-delay-ms = <200>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&dt_emmc1_pins>; ++ status = "okay"; ++ }; ++ }; ++ ++ //ptc ++ fragment@4 { ++ target-path = "/soc/pwm@120d0000"; ++ __overlay__ { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&dt_pwm_ch6to7_pins>; ++ status = "okay"; ++ }; ++ }; ++}; ++ +--- /dev/null ++++ b/arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-uart5-pwm.dtso +@@ -0,0 +1,92 @@ ++/dts-v1/; ++/plugin/; ++#include ++#include "../jh7110-pinfunc.h" ++/ { ++ compatible = "starfive,jh7110"; ++ ++ //sysgpio ++ fragment@0 { ++ target-path = "/soc/pinctrl@13040000"; ++ __overlay__ { ++ dt_pwm_ch0to3_pins: dt-pwm-ch0to3-0 { ++ pwm-pins { ++ pinmux = , ++ , ++ , ++ ; ++ drive-strength = <12>; ++ }; ++ }; ++ }; ++ }; ++ ++ //aongpio ++ fragment@1 { ++ target-path = "/soc/pinctrl@17020000"; ++ __overlay__ { ++ dt_pwm_ch4to5_pins: dt-pwm-ch4to5-0 { ++ pwm-pins { ++ pinmux = , ++ ; ++ drive-strength = <12>; ++ }; ++ }; ++ }; ++ }; ++ ++ //uart5 ++ fragment@2 { ++ target-path = "/soc/serial@12020000"; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ //ptc ++ fragment@3 { ++ target-path = "/soc/pwm@120d0000"; ++ __overlay__ { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&dt_pwm_ch0to3_pins &dt_pwm_ch4to5_pins>; ++ status = "okay"; ++ }; ++ }; ++ ++ //i2c0 ++ fragment@4 { ++ target-path = "/soc/i2c@10030000"; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ //i2c1 ++ fragment@5 { ++ target-path = "/soc/i2c@10040000"; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ //i2c3 ++ fragment@6 { ++ target-path = "/soc/i2c@12030000"; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++}; ++ diff --git a/target/linux/starfive/patches-6.6/0059-riscv-configs-Add-starfive_jh7110_defconfig.patch b/target/linux/starfive/patches-6.6/0059-riscv-configs-Add-starfive_jh7110_defconfig.patch new file mode 100644 index 0000000000..ddf72454eb --- /dev/null +++ b/target/linux/starfive/patches-6.6/0059-riscv-configs-Add-starfive_jh7110_defconfig.patch @@ -0,0 +1,385 @@ +From 5c888fa081caf5d9473e733931d1c7b3d4b61e61 Mon Sep 17 00:00:00 2001 +From: Hal Feng +Date: Fri, 28 Jul 2023 18:42:55 +0800 +Subject: [PATCH 059/116] riscv: configs: Add starfive_jh7110_defconfig + +Add starfive_jh7110_defconfig for JH7110 EVB. +The code is ported from tag JH7110_SDK_6.1_v5.11.3 + +Signed-off-by: Hal Feng +--- + arch/riscv/configs/starfive_jh7110_defconfig | 368 +++++++++++++++++++ + 1 file changed, 368 insertions(+) + create mode 100644 arch/riscv/configs/starfive_jh7110_defconfig + +--- /dev/null ++++ b/arch/riscv/configs/starfive_jh7110_defconfig +@@ -0,0 +1,368 @@ ++CONFIG_COMPILE_TEST=y ++# CONFIG_WERROR is not set ++CONFIG_DEFAULT_HOSTNAME="StarFive" ++CONFIG_SYSVIPC=y ++CONFIG_POSIX_MQUEUE=y ++CONFIG_USELIB=y ++CONFIG_NO_HZ_IDLE=y ++CONFIG_HIGH_RES_TIMERS=y ++CONFIG_BPF_SYSCALL=y ++CONFIG_IKCONFIG=y ++CONFIG_IKCONFIG_PROC=y ++CONFIG_CGROUPS=y ++CONFIG_MEMCG=y ++CONFIG_CGROUP_SCHED=y ++CONFIG_CFS_BANDWIDTH=y ++CONFIG_RT_GROUP_SCHED=y ++CONFIG_CGROUP_PIDS=y ++CONFIG_CGROUP_FREEZER=y ++CONFIG_CGROUP_HUGETLB=y ++CONFIG_CPUSETS=y ++CONFIG_CGROUP_DEVICE=y ++CONFIG_CGROUP_CPUACCT=y ++CONFIG_CGROUP_PERF=y ++CONFIG_CGROUP_BPF=y ++CONFIG_NAMESPACES=y ++CONFIG_USER_NS=y ++CONFIG_CHECKPOINT_RESTORE=y ++CONFIG_BLK_DEV_INITRD=y ++CONFIG_EXPERT=y ++# CONFIG_SYSFS_SYSCALL is not set ++CONFIG_PROFILING=y ++CONFIG_SOC_MICROCHIP_POLARFIRE=y ++CONFIG_SOC_STARFIVE=y ++CONFIG_SOC_VIRT=y ++CONFIG_ERRATA_SIFIVE=y ++CONFIG_NONPORTABLE=y ++CONFIG_SMP=y ++CONFIG_RISCV_SBI_V01=y ++# CONFIG_RISCV_BOOT_SPINWAIT is not set ++CONFIG_HIBERNATION=y ++CONFIG_PM_STD_PARTITION="PARTLABEL=hibernation" ++CONFIG_PM_DEBUG=y ++CONFIG_PM_ADVANCED_DEBUG=y ++CONFIG_PM_TEST_SUSPEND=y ++CONFIG_ENERGY_MODEL=y ++CONFIG_CPU_IDLE=y ++CONFIG_CPU_FREQ=y ++CONFIG_CPU_FREQ_STAT=y ++CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y ++CONFIG_CPU_FREQ_GOV_POWERSAVE=y ++CONFIG_CPU_FREQ_GOV_USERSPACE=y ++CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y ++CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y ++CONFIG_CPUFREQ_DT=y ++CONFIG_VIRTUALIZATION=y ++CONFIG_KVM=m ++CONFIG_JUMP_LABEL=y ++CONFIG_MODULES=y ++CONFIG_MODULE_UNLOAD=y ++CONFIG_BINFMT_MISC=y ++CONFIG_CMA=y ++CONFIG_NET=y ++CONFIG_PACKET=y ++CONFIG_XFRM_USER=m ++CONFIG_IP_MULTICAST=y ++CONFIG_IP_ADVANCED_ROUTER=y ++CONFIG_IP_MULTIPLE_TABLES=y ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++CONFIG_IP_PNP_BOOTP=y ++CONFIG_IP_PNP_RARP=y ++CONFIG_INET_ESP=m ++CONFIG_NETFILTER=y ++CONFIG_BRIDGE_NETFILTER=m ++CONFIG_NF_CONNTRACK=m ++CONFIG_NF_CONNTRACK_FTP=m ++CONFIG_NF_CONNTRACK_TFTP=m ++CONFIG_NETFILTER_XT_MARK=m ++CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=m ++CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m ++CONFIG_NETFILTER_XT_MATCH_IPVS=m ++CONFIG_IP_VS=m ++CONFIG_IP_VS_PROTO_TCP=y ++CONFIG_IP_VS_PROTO_UDP=y ++CONFIG_IP_VS_RR=m ++CONFIG_IP_VS_NFCT=y ++CONFIG_NF_LOG_ARP=m ++CONFIG_NF_LOG_IPV4=m ++CONFIG_IP_NF_IPTABLES=m ++CONFIG_IP_NF_FILTER=m ++CONFIG_IP_NF_TARGET_REJECT=m ++CONFIG_IP_NF_NAT=m ++CONFIG_IP_NF_TARGET_MASQUERADE=m ++CONFIG_IP_NF_TARGET_REDIRECT=m ++CONFIG_IP_NF_MANGLE=m ++CONFIG_NF_LOG_IPV6=m ++CONFIG_IP6_NF_IPTABLES=m ++CONFIG_IP6_NF_MATCH_IPV6HEADER=m ++CONFIG_IP6_NF_FILTER=m ++CONFIG_IP6_NF_TARGET_REJECT=m ++CONFIG_IP6_NF_MANGLE=m ++CONFIG_BRIDGE=m ++CONFIG_BRIDGE_VLAN_FILTERING=y ++CONFIG_VLAN_8021Q=m ++CONFIG_NET_SCHED=y ++CONFIG_NET_CLS_CGROUP=m ++CONFIG_NETLINK_DIAG=y ++CONFIG_CGROUP_NET_PRIO=y ++CONFIG_CAN=y ++CONFIG_BT=y ++CONFIG_BT_RFCOMM=y ++CONFIG_BT_RFCOMM_TTY=y ++CONFIG_BT_BNEP=y ++CONFIG_BT_BNEP_MC_FILTER=y ++CONFIG_BT_BNEP_PROTO_FILTER=y ++CONFIG_BT_HCIUART=y ++CONFIG_BT_HCIUART_H4=y ++CONFIG_CFG80211=y ++CONFIG_MAC80211=y ++CONFIG_RFKILL=y ++CONFIG_NET_9P=y ++CONFIG_NET_9P_VIRTIO=y ++CONFIG_PCI=y ++CONFIG_PCIEPORTBUS=y ++# CONFIG_PCIEASPM is not set ++CONFIG_PCI_HOST_GENERIC=y ++CONFIG_PCIE_FU740=y ++CONFIG_PCIE_STARFIVE_HOST=y ++CONFIG_DEVTMPFS=y ++CONFIG_DEVTMPFS_MOUNT=y ++CONFIG_MTD=y ++CONFIG_MTD_BLOCK=y ++CONFIG_MTD_SPI_NOR=y ++CONFIG_OF_CONFIGFS=y ++CONFIG_BLK_DEV_LOOP=y ++CONFIG_VIRTIO_BLK=y ++CONFIG_BLK_DEV_NVME=y ++CONFIG_BLK_DEV_SD=y ++CONFIG_BLK_DEV_SR=y ++CONFIG_SCSI_VIRTIO=y ++CONFIG_ATA=y ++CONFIG_SATA_AHCI=y ++CONFIG_SATA_AHCI_PLATFORM=y ++CONFIG_MD=y ++CONFIG_BLK_DEV_DM=m ++CONFIG_DM_THIN_PROVISIONING=m ++CONFIG_NETDEVICES=y ++CONFIG_DUMMY=m ++CONFIG_MACVLAN=m ++CONFIG_IPVLAN=m ++CONFIG_VXLAN=m ++CONFIG_VETH=m ++CONFIG_VIRTIO_NET=y ++CONFIG_MACB=y ++CONFIG_E1000E=y ++CONFIG_R8169=y ++CONFIG_STMMAC_ETH=y ++CONFIG_DWMAC_DWC_QOS_ETH=y ++# CONFIG_DWMAC_GENERIC is not set ++CONFIG_DWMAC_STARFIVE=y ++CONFIG_MARVELL_PHY=y ++CONFIG_MICREL_PHY=y ++CONFIG_MICROCHIP_PHY=y ++CONFIG_MICROSEMI_PHY=y ++CONFIG_MOTORCOMM_PHY=y ++CONFIG_IPMS_CAN=y ++CONFIG_IWLWIFI=y ++CONFIG_IWLDVM=y ++CONFIG_IWLMVM=y ++CONFIG_INPUT_MOUSEDEV=y ++CONFIG_INPUT_EVDEV=y ++CONFIG_INPUT_TOUCHSCREEN=y ++CONFIG_TOUCHSCREEN_TINKER_FT5406=y ++CONFIG_SERIAL_8250=y ++CONFIG_SERIAL_8250_CONSOLE=y ++CONFIG_SERIAL_8250_NR_UARTS=6 ++CONFIG_SERIAL_8250_RUNTIME_UARTS=6 ++CONFIG_SERIAL_8250_EXTENDED=y ++CONFIG_SERIAL_8250_MANY_PORTS=y ++CONFIG_SERIAL_8250_DW=y ++CONFIG_SERIAL_OF_PLATFORM=y ++CONFIG_SERIAL_EARLYCON_RISCV_SBI=y ++CONFIG_TTY_PRINTK=y ++CONFIG_VIRTIO_CONSOLE=y ++CONFIG_HW_RANDOM=y ++CONFIG_HW_RANDOM_VIRTIO=y ++CONFIG_HW_RANDOM_JH7110=y ++CONFIG_I2C_CHARDEV=y ++CONFIG_I2C_DESIGNWARE_PLATFORM=y ++CONFIG_SPI=y ++CONFIG_SPI_CADENCE_QUADSPI=y ++CONFIG_SPI_PL022=y ++CONFIG_SPI_SIFIVE=y ++CONFIG_SPI_SPIDEV=y ++# CONFIG_PTP_1588_CLOCK is not set ++CONFIG_GPIO_SYSFS=y ++CONFIG_GPIO_SIFIVE=y ++CONFIG_SENSORS_SFCTEMP=y ++CONFIG_THERMAL=y ++CONFIG_THERMAL_WRITABLE_TRIPS=y ++CONFIG_CPU_THERMAL=y ++CONFIG_THERMAL_EMULATION=y ++CONFIG_WATCHDOG=y ++CONFIG_WATCHDOG_SYSFS=y ++CONFIG_MFD_AXP20X_I2C=y ++CONFIG_REGULATOR=y ++CONFIG_REGULATOR_AXP20X=y ++CONFIG_REGULATOR_STARFIVE_JH7110=y ++# CONFIG_MEDIA_CEC_SUPPORT is not set ++CONFIG_MEDIA_SUPPORT=y ++CONFIG_MEDIA_USB_SUPPORT=y ++CONFIG_USB_VIDEO_CLASS=y ++CONFIG_V4L_PLATFORM_DRIVERS=y ++CONFIG_V4L_MEM2MEM_DRIVERS=y ++CONFIG_VIDEO_WAVE_VPU=m ++CONFIG_VIN_SENSOR_SC2235=y ++CONFIG_VIN_SENSOR_OV4689=y ++CONFIG_VIN_SENSOR_IMX219=y ++CONFIG_VIDEO_STF_VIN=y ++CONFIG_VIDEO_IMX708=y ++CONFIG_DRM_I2C_NXP_TDA998X=y ++CONFIG_DRM_I2C_NXP_TDA9950=y ++CONFIG_DRM_RADEON=m ++CONFIG_DRM_VIRTIO_GPU=m ++CONFIG_DRM_VERISILICON=y ++CONFIG_STARFIVE_INNO_HDMI=y ++CONFIG_STARFIVE_DSI=y ++CONFIG_DRM_IMG_ROGUE=y ++CONFIG_DRM_LEGACY=y ++CONFIG_FB=y ++CONFIG_SOUND=y ++CONFIG_SND=y ++CONFIG_SND_USB_AUDIO=y ++CONFIG_SND_SOC=y ++CONFIG_SND_DESIGNWARE_I2S=y ++# CONFIG_SND_SOC_INTEL_SST_TOPLEVEL is not set ++CONFIG_SND_SOC_STARFIVE=y ++CONFIG_SND_SOC_JH7110_PDM=y ++CONFIG_SND_SOC_JH7110_PWMDAC=y ++CONFIG_SND_SOC_JH7110_SPDIF=y ++CONFIG_SND_SOC_JH7110_TDM=y ++CONFIG_SND_SOC_AC108=y ++CONFIG_SND_SOC_WM8960=y ++CONFIG_SND_SIMPLE_CARD=y ++CONFIG_USB=y ++CONFIG_USB_XHCI_HCD=y ++CONFIG_USB_EHCI_HCD=y ++CONFIG_USB_EHCI_HCD_PLATFORM=y ++CONFIG_USB_OHCI_HCD=y ++CONFIG_USB_OHCI_HCD_PLATFORM=y ++CONFIG_USB_STORAGE=y ++CONFIG_USB_UAS=y ++CONFIG_USB_CDNS_SUPPORT=y ++CONFIG_USB_CDNS3=y ++CONFIG_USB_CDNS3_GADGET=y ++CONFIG_USB_CDNS3_HOST=y ++CONFIG_USB_CDNS3_STARFIVE=y ++CONFIG_USB_GADGET=y ++CONFIG_USB_CONFIGFS=y ++CONFIG_USB_CONFIGFS_MASS_STORAGE=y ++CONFIG_USB_CONFIGFS_F_FS=y ++CONFIG_MMC=y ++CONFIG_MMC_SDHCI=y ++CONFIG_MMC_SDHCI_PLTFM=y ++CONFIG_MMC_SDHCI_CADENCE=y ++CONFIG_MMC_SPI=y ++CONFIG_MMC_DW=y ++CONFIG_MMC_DW_STARFIVE=y ++CONFIG_RTC_CLASS=y ++# CONFIG_RTC_DRV_SPEAR is not set ++CONFIG_RTC_DRV_STARFIVE=y ++CONFIG_DMADEVICES=y ++CONFIG_AMBA_PL08X=y ++CONFIG_DW_AXI_DMAC=y ++# CONFIG_SH_DMAE_BASE is not set ++# CONFIG_TI_EDMA is not set ++# CONFIG_DMA_OMAP is not set ++CONFIG_DMATEST=y ++CONFIG_VIRTIO_PCI=y ++CONFIG_VIRTIO_BALLOON=y ++CONFIG_VIRTIO_INPUT=y ++CONFIG_VIRTIO_MMIO=y ++CONFIG_STAGING=y ++CONFIG_STAGING_MEDIA=y ++CONFIG_CLK_STARFIVE_JH7110_AON=y ++CONFIG_CLK_STARFIVE_JH7110_STG=y ++CONFIG_CLK_STARFIVE_JH7110_ISP=y ++CONFIG_CLK_STARFIVE_JH7110_VOUT=y ++CONFIG_MAILBOX=y ++CONFIG_STARFIVE_MBOX=m ++CONFIG_STARFIVE_MBOX_TEST=m ++CONFIG_RPMSG_CHAR=y ++CONFIG_RPMSG_CTRL=y ++CONFIG_RPMSG_VIRTIO=y ++CONFIG_SIFIVE_CCACHE=y ++CONFIG_PWM=y ++CONFIG_PWM_OCORES=y ++CONFIG_PHY_STARFIVE_JH7110_PCIE=y ++CONFIG_PHY_STARFIVE_JH7110_USB=y ++CONFIG_PHY_M31_DPHY_RX0=y ++CONFIG_EXT4_FS=y ++CONFIG_EXT4_FS_POSIX_ACL=y ++CONFIG_EXT4_FS_SECURITY=y ++CONFIG_BTRFS_FS=m ++CONFIG_BTRFS_FS_POSIX_ACL=y ++CONFIG_AUTOFS_FS=y ++CONFIG_FUSE_FS=y ++CONFIG_OVERLAY_FS=y ++CONFIG_OVERLAY_FS_INDEX=y ++CONFIG_OVERLAY_FS_XINO_AUTO=y ++CONFIG_OVERLAY_FS_METACOPY=y ++CONFIG_ISO9660_FS=y ++CONFIG_JOLIET=y ++CONFIG_ZISOFS=y ++CONFIG_MSDOS_FS=y ++CONFIG_VFAT_FS=y ++CONFIG_EXFAT_FS=y ++CONFIG_TMPFS=y ++CONFIG_TMPFS_POSIX_ACL=y ++CONFIG_HUGETLBFS=y ++CONFIG_JFFS2_FS=y ++CONFIG_NFS_FS=y ++CONFIG_NFS_V4=y ++CONFIG_NFS_V4_1=y ++CONFIG_NFS_V4_2=y ++CONFIG_ROOT_NFS=y ++CONFIG_9P_FS=y ++CONFIG_NLS_CODEPAGE_437=y ++CONFIG_NLS_ISO8859_1=m ++CONFIG_SECURITY=y ++CONFIG_SECURITY_SELINUX=y ++CONFIG_SECURITY_APPARMOR=y ++CONFIG_DEFAULT_SECURITY_DAC=y ++CONFIG_LSM="" ++CONFIG_INIT_STACK_NONE=y ++CONFIG_CRYPTO_USER=y ++CONFIG_CRYPTO_ZSTD=y ++CONFIG_CRYPTO_USER_API_HASH=y ++CONFIG_CRYPTO_USER_API_SKCIPHER=y ++CONFIG_CRYPTO_USER_API_AEAD=y ++CONFIG_CRYPTO_STATS=y ++CONFIG_CRYPTO_DEV_VIRTIO=y ++CONFIG_CRYPTO_DEV_JH7110=y ++CONFIG_DMA_CMA=y ++CONFIG_PRINTK_TIME=y ++CONFIG_DEBUG_FS=y ++CONFIG_DEBUG_PAGEALLOC=y ++CONFIG_SCHED_STACK_END_CHECK=y ++CONFIG_DEBUG_VM=y ++CONFIG_DEBUG_VM_PGFLAGS=y ++CONFIG_DEBUG_MEMORY_INIT=y ++CONFIG_DEBUG_PER_CPU_MAPS=y ++CONFIG_SOFTLOCKUP_DETECTOR=y ++CONFIG_WQ_WATCHDOG=y ++CONFIG_DEBUG_TIMEKEEPING=y ++CONFIG_DEBUG_RT_MUTEXES=y ++CONFIG_DEBUG_SPINLOCK=y ++CONFIG_DEBUG_MUTEXES=y ++CONFIG_DEBUG_RWSEMS=y ++CONFIG_DEBUG_LIST=y ++CONFIG_DEBUG_PLIST=y ++CONFIG_DEBUG_SG=y ++CONFIG_RCU_CPU_STALL_TIMEOUT=60 ++# CONFIG_RCU_TRACE is not set ++CONFIG_RCU_EQS_DEBUG=y ++# CONFIG_FTRACE is not set ++# CONFIG_RUNTIME_TESTING_MENU is not set ++CONFIG_MEMTEST=y diff --git a/target/linux/starfive/patches-6.6/0060-of-configfs-Add-configfs-function.patch b/target/linux/starfive/patches-6.6/0060-of-configfs-Add-configfs-function.patch new file mode 100644 index 0000000000..68deb70c9b --- /dev/null +++ b/target/linux/starfive/patches-6.6/0060-of-configfs-Add-configfs-function.patch @@ -0,0 +1,318 @@ +From 95c702022f5e4cb786719fcf90170334b1e562cc Mon Sep 17 00:00:00 2001 +From: Jianlong Huang +Date: Thu, 16 Jun 2022 17:13:57 +0800 +Subject: [PATCH 060/116] of: configfs: Add configfs function + +Signed-off-by: Jianlong Huang +Signed-off-by: Hal Feng +--- + drivers/of/Kconfig | 7 ++ + drivers/of/Makefile | 1 + + drivers/of/configfs.c | 277 ++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 285 insertions(+) + create mode 100644 drivers/of/configfs.c + +--- a/drivers/of/Kconfig ++++ b/drivers/of/Kconfig +@@ -102,4 +102,11 @@ config OF_OVERLAY + config OF_NUMA + bool + ++config OF_CONFIGFS ++ bool "Device Tree Overlay ConfigFS interface" ++ select CONFIGFS_FS ++ select OF_OVERLAY ++ help ++ Enable a simple user-space driven DT overlay interface. ++ + endif # OF +--- a/drivers/of/Makefile ++++ b/drivers/of/Makefile +@@ -11,6 +11,7 @@ obj-$(CONFIG_OF_UNITTEST) += unittest.o + obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o + obj-$(CONFIG_OF_RESOLVE) += resolver.o + obj-$(CONFIG_OF_OVERLAY) += overlay.o ++obj-$(CONFIG_OF_CONFIGFS) += configfs.o + obj-$(CONFIG_OF_NUMA) += of_numa.o + + ifdef CONFIG_KEXEC_FILE +--- /dev/null ++++ b/drivers/of/configfs.c +@@ -0,0 +1,277 @@ ++/* ++ * Configfs entries for device-tree ++ * ++ * Copyright (C) 2013 - Pantelis Antoniou ++ * ++ * 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; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "of_private.h" ++ ++struct cfs_overlay_item { ++ struct config_item item; ++ ++ char path[PATH_MAX]; ++ ++ const struct firmware *fw; ++ struct device_node *overlay; ++ int ov_id; ++ ++ void *dtbo; ++ int dtbo_size; ++}; ++ ++static inline struct cfs_overlay_item *to_cfs_overlay_item( ++ struct config_item *item) ++{ ++ return item ? container_of(item, struct cfs_overlay_item, item) : NULL; ++} ++ ++static ssize_t cfs_overlay_item_path_show(struct config_item *item, ++ char *page) ++{ ++ struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); ++ return sprintf(page, "%s\n", overlay->path); ++} ++ ++static ssize_t cfs_overlay_item_path_store(struct config_item *item, ++ const char *page, size_t count) ++{ ++ struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); ++ const char *p = page; ++ char *s; ++ int err; ++ ++ /* if it's set do not allow changes */ ++ if (overlay->path[0] != '\0' || overlay->dtbo_size > 0) ++ return -EPERM; ++ ++ /* copy to path buffer (and make sure it's always zero terminated */ ++ count = snprintf(overlay->path, sizeof(overlay->path) - 1, "%s", p); ++ overlay->path[sizeof(overlay->path) - 1] = '\0'; ++ ++ /* strip trailing newlines */ ++ s = overlay->path + strlen(overlay->path); ++ while (s > overlay->path && *--s == '\n') ++ *s = '\0'; ++ ++ pr_debug("%s: path is '%s'\n", __func__, overlay->path); ++ ++ err = request_firmware(&overlay->fw, overlay->path, NULL); ++ if (err != 0) ++ return err; ++ ++ err = of_overlay_fdt_apply((void *)overlay->fw->data, ++ (u32)overlay->fw->size, &overlay->ov_id, NULL); ++ if (err != 0) ++ goto out_err; ++ ++ return count; ++ ++out_err: ++ ++ release_firmware(overlay->fw); ++ overlay->fw = NULL; ++ ++ overlay->path[0] = '\0'; ++ return err; ++} ++ ++static ssize_t cfs_overlay_item_status_show(struct config_item *item, ++ char *page) ++{ ++ struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); ++ ++ return sprintf(page, "%s\n", ++ overlay->ov_id > 0 ? "applied" : "unapplied"); ++} ++ ++CONFIGFS_ATTR(cfs_overlay_item_, path); ++CONFIGFS_ATTR_RO(cfs_overlay_item_, status); ++ ++static struct configfs_attribute *cfs_overlay_attrs[] = { ++ &cfs_overlay_item_attr_path, ++ &cfs_overlay_item_attr_status, ++ NULL, ++}; ++ ++ssize_t cfs_overlay_item_dtbo_read(struct config_item *item, ++ void *buf, size_t max_count) ++{ ++ struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); ++ ++ pr_debug("%s: buf=%p max_count=%zu\n", __func__, ++ buf, max_count); ++ ++ if (overlay->dtbo == NULL) ++ return 0; ++ ++ /* copy if buffer provided */ ++ if (buf != NULL) { ++ /* the buffer must be large enough */ ++ if (overlay->dtbo_size > max_count) ++ return -ENOSPC; ++ ++ memcpy(buf, overlay->dtbo, overlay->dtbo_size); ++ } ++ ++ return overlay->dtbo_size; ++} ++ ++ssize_t cfs_overlay_item_dtbo_write(struct config_item *item, ++ const void *buf, size_t count) ++{ ++ struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); ++ int err; ++ ++ /* if it's set do not allow changes */ ++ if (overlay->path[0] != '\0' || overlay->dtbo_size > 0) ++ return -EPERM; ++ ++ /* copy the contents */ ++ overlay->dtbo = kmemdup(buf, count, GFP_KERNEL); ++ if (overlay->dtbo == NULL) ++ return -ENOMEM; ++ ++ overlay->dtbo_size = count; ++ ++ err = of_overlay_fdt_apply(overlay->dtbo, overlay->dtbo_size, ++ &overlay->ov_id, NULL); ++ if (err != 0) ++ goto out_err; ++ ++ return count; ++ ++out_err: ++ kfree(overlay->dtbo); ++ overlay->dtbo = NULL; ++ overlay->dtbo_size = 0; ++ overlay->ov_id = 0; ++ ++ return err; ++} ++ ++CONFIGFS_BIN_ATTR(cfs_overlay_item_, dtbo, NULL, SZ_1M); ++ ++static struct configfs_bin_attribute *cfs_overlay_bin_attrs[] = { ++ &cfs_overlay_item_attr_dtbo, ++ NULL, ++}; ++ ++static void cfs_overlay_release(struct config_item *item) ++{ ++ struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); ++ ++ if (overlay->ov_id > 0) ++ of_overlay_remove(&overlay->ov_id); ++ if (overlay->fw) ++ release_firmware(overlay->fw); ++ /* kfree with NULL is safe */ ++ kfree(overlay->dtbo); ++ kfree(overlay); ++} ++ ++static struct configfs_item_operations cfs_overlay_item_ops = { ++ .release = cfs_overlay_release, ++}; ++ ++static struct config_item_type cfs_overlay_type = { ++ .ct_item_ops = &cfs_overlay_item_ops, ++ .ct_attrs = cfs_overlay_attrs, ++ .ct_bin_attrs = cfs_overlay_bin_attrs, ++ .ct_owner = THIS_MODULE, ++}; ++ ++static struct config_item *cfs_overlay_group_make_item( ++ struct config_group *group, const char *name) ++{ ++ struct cfs_overlay_item *overlay; ++ ++ overlay = kzalloc(sizeof(*overlay), GFP_KERNEL); ++ if (!overlay) ++ return ERR_PTR(-ENOMEM); ++ ++ config_item_init_type_name(&overlay->item, name, &cfs_overlay_type); ++ return &overlay->item; ++} ++ ++static void cfs_overlay_group_drop_item(struct config_group *group, ++ struct config_item *item) ++{ ++ struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); ++ ++ config_item_put(&overlay->item); ++} ++ ++static struct configfs_group_operations overlays_ops = { ++ .make_item = cfs_overlay_group_make_item, ++ .drop_item = cfs_overlay_group_drop_item, ++}; ++ ++static struct config_item_type overlays_type = { ++ .ct_group_ops = &overlays_ops, ++ .ct_owner = THIS_MODULE, ++}; ++ ++static struct configfs_group_operations of_cfs_ops = { ++ /* empty - we don't allow anything to be created */ ++}; ++ ++static struct config_item_type of_cfs_type = { ++ .ct_group_ops = &of_cfs_ops, ++ .ct_owner = THIS_MODULE, ++}; ++ ++struct config_group of_cfs_overlay_group; ++ ++static struct configfs_subsystem of_cfs_subsys = { ++ .su_group = { ++ .cg_item = { ++ .ci_namebuf = "device-tree", ++ .ci_type = &of_cfs_type, ++ }, ++ }, ++ .su_mutex = __MUTEX_INITIALIZER(of_cfs_subsys.su_mutex), ++}; ++ ++static int __init of_cfs_init(void) ++{ ++ int ret; ++ ++ pr_info("%s\n", __func__); ++ ++ config_group_init(&of_cfs_subsys.su_group); ++ config_group_init_type_name(&of_cfs_overlay_group, "overlays", ++ &overlays_type); ++ configfs_add_default_group(&of_cfs_overlay_group, ++ &of_cfs_subsys.su_group); ++ ++ ret = configfs_register_subsystem(&of_cfs_subsys); ++ if (ret != 0) { ++ pr_err("%s: failed to register subsys\n", __func__); ++ goto out; ++ } ++ pr_info("%s: OK\n", __func__); ++out: ++ return ret; ++} ++late_initcall(of_cfs_init); diff --git a/target/linux/starfive/patches-6.6/0061-usr-Add-gen_initramfs_list.sh.patch b/target/linux/starfive/patches-6.6/0061-usr-Add-gen_initramfs_list.sh.patch new file mode 100644 index 0000000000..ade0f467a0 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0061-usr-Add-gen_initramfs_list.sh.patch @@ -0,0 +1,344 @@ +From 7891826f8c2de9ee0f6459cf969f7b082e29b154 Mon Sep 17 00:00:00 2001 +From: Hal Feng +Date: Thu, 1 Jun 2023 23:10:09 -0700 +Subject: [PATCH 061/116] usr: Add gen_initramfs_list.sh + +Add gen_initramfs_list.sh + +Signed-off-by: Hal Feng +--- + usr/gen_initramfs_list.sh | 328 ++++++++++++++++++++++++++++++++++++++ + 1 file changed, 328 insertions(+) + create mode 100644 usr/gen_initramfs_list.sh + +--- /dev/null ++++ b/usr/gen_initramfs_list.sh +@@ -0,0 +1,328 @@ ++#!/bin/sh ++# Copyright (C) Martin Schlemmer ++# Copyright (C) 2006 Sam Ravnborg ++# ++# Released under the terms of the GNU GPL ++# ++# Generate a cpio packed initramfs. It uses gen_init_cpio to generate ++# the cpio archive, and then compresses it. ++# The script may also be used to generate the inputfile used for gen_init_cpio ++# This script assumes that gen_init_cpio is located in usr/ directory ++ ++# error out on errors ++set -e ++ ++usage() { ++cat << EOF ++Usage: ++$0 [-o ] [-u ] [-g ] {-d | } ... ++ -o Create compressed initramfs file named using ++ gen_init_cpio and compressor depending on the extension ++ -u User ID to map to user ID 0 (root). ++ is only meaningful if is a ++ directory. "squash" forces all files to uid 0. ++ -g Group ID to map to group ID 0 (root). ++ is only meaningful if is a ++ directory. "squash" forces all files to gid 0. ++ File list or directory for cpio archive. ++ If is a .cpio file it will be used ++ as direct input to initramfs. ++ -d Output the default cpio list. ++ ++All options except -o and -l may be repeated and are interpreted ++sequentially and immediately. -u and -g states are preserved across ++ options so an explicit "-u 0 -g 0" is required ++to reset the root/group mapping. ++EOF ++} ++ ++# awk style field access ++# $1 - field number; rest is argument string ++field() { ++ shift $1 ; echo $1 ++} ++ ++list_default_initramfs() { ++ # echo usr/kinit/kinit ++ : ++} ++ ++default_initramfs() { ++ cat <<-EOF >> ${output} ++ # This is a very simple, default initramfs ++ ++ dir /dev 0755 0 0 ++ nod /dev/console 0600 0 0 c 5 1 ++ dir /root 0700 0 0 ++ # file /kinit usr/kinit/kinit 0755 0 0 ++ # slink /init kinit 0755 0 0 ++ EOF ++} ++ ++filetype() { ++ local argv1="$1" ++ ++ # symlink test must come before file test ++ if [ -L "${argv1}" ]; then ++ echo "slink" ++ elif [ -f "${argv1}" ]; then ++ echo "file" ++ elif [ -d "${argv1}" ]; then ++ echo "dir" ++ elif [ -b "${argv1}" -o -c "${argv1}" ]; then ++ echo "nod" ++ elif [ -p "${argv1}" ]; then ++ echo "pipe" ++ elif [ -S "${argv1}" ]; then ++ echo "sock" ++ else ++ echo "invalid" ++ fi ++ return 0 ++} ++ ++list_print_mtime() { ++ : ++} ++ ++print_mtime() { ++ local my_mtime="0" ++ ++ if [ -e "$1" ]; then ++ my_mtime=$(find "$1" -printf "%T@\n" | sort -r | head -n 1) ++ fi ++ ++ echo "# Last modified: ${my_mtime}" >> ${output} ++ echo "" >> ${output} ++} ++ ++list_parse() { ++ if [ -L "$1" ]; then ++ return ++ fi ++ echo "$1" | sed 's/:/\\:/g; s/$/ \\/' ++} ++ ++# for each file print a line in following format ++# ++# for links, devices etc the format differs. See gen_init_cpio for details ++parse() { ++ local location="$1" ++ local name="/${location#${srcdir}}" ++ # change '//' into '/' ++ name=$(echo "$name" | sed -e 's://*:/:g') ++ local mode="$2" ++ local uid="$3" ++ local gid="$4" ++ local ftype=$(filetype "${location}") ++ # remap uid/gid to 0 if necessary ++ [ "$root_uid" = "squash" ] && uid=0 || [ "$uid" -eq "$root_uid" ] && uid=0 ++ [ "$root_gid" = "squash" ] && gid=0 || [ "$gid" -eq "$root_gid" ] && gid=0 ++ local str="${mode} ${uid} ${gid}" ++ ++ [ "${ftype}" = "invalid" ] && return 0 ++ [ "${location}" = "${srcdir}" ] && return 0 ++ ++ case "${ftype}" in ++ "file") ++ str="${ftype} ${name} ${location} ${str}" ++ ;; ++ "nod") ++ local dev=`LC_ALL=C ls -l "${location}"` ++ local maj=`field 5 ${dev}` ++ local min=`field 6 ${dev}` ++ maj=${maj%,} ++ ++ [ -b "${location}" ] && dev="b" || dev="c" ++ ++ str="${ftype} ${name} ${str} ${dev} ${maj} ${min}" ++ ;; ++ "slink") ++ local target=`readlink "${location}"` ++ str="${ftype} ${name} ${target} ${str}" ++ ;; ++ *) ++ str="${ftype} ${name} ${str}" ++ ;; ++ esac ++ ++ echo "${str}" >> ${output} ++ ++ return 0 ++} ++ ++unknown_option() { ++ printf "ERROR: unknown option \"$arg\"\n" >&2 ++ printf "If the filename validly begins with '-', " >&2 ++ printf "then it must be prefixed\n" >&2 ++ printf "by './' so that it won't be interpreted as an option." >&2 ++ printf "\n" >&2 ++ usage >&2 ++ exit 1 ++} ++ ++list_header() { ++ : ++} ++ ++header() { ++ printf "\n#####################\n# $1\n" >> ${output} ++} ++ ++# process one directory (incl sub-directories) ++dir_filelist() { ++ ${dep_list}header "$1" ++ ++ srcdir=$(echo "$1" | sed -e 's://*:/:g') ++ dirlist=$(find "${srcdir}" -printf "%p %m %U %G\n" | LANG=C sort) ++ ++ # If $dirlist is only one line, then the directory is empty ++ if [ "$(echo "${dirlist}" | wc -l)" -gt 1 ]; then ++ ${dep_list}print_mtime "$1" ++ ++ echo "${dirlist}" | \ ++ while read x; do ++ ${dep_list}parse ${x} ++ done ++ fi ++} ++ ++# if only one file is specified and it is .cpio file then use it direct as fs ++# if a directory is specified then add all files in given direcotry to fs ++# if a regular file is specified assume it is in gen_initramfs format ++input_file() { ++ source="$1" ++ if [ -f "$1" ]; then ++ ${dep_list}header "$1" ++ is_cpio="$(echo "$1" | sed 's/^.*\.cpio\(\..*\)\{0,1\}/cpio/')" ++ if [ $2 -eq 0 -a ${is_cpio} = "cpio" ]; then ++ cpio_file=$1 ++ echo "$1" | grep -q '^.*\.cpio\..*' && is_cpio_compressed="compressed" ++ [ ! -z ${dep_list} ] && echo "$1" ++ return 0 ++ fi ++ if [ -z ${dep_list} ]; then ++ print_mtime "$1" >> ${output} ++ cat "$1" >> ${output} ++ else ++ echo "$1 \\" ++ cat "$1" | while read type dir file perm ; do ++ if [ "$type" = "file" ]; then ++ echo "$file \\"; ++ fi ++ done ++ fi ++ elif [ -d "$1" ]; then ++ dir_filelist "$1" ++ else ++ echo " ${prog}: Cannot open '$1'" >&2 ++ exit 1 ++ fi ++} ++ ++prog=$0 ++root_uid=0 ++root_gid=0 ++dep_list= ++cpio_file= ++cpio_list= ++output="/dev/stdout" ++output_file="" ++is_cpio_compressed= ++compr="gzip -n -9 -f" ++ ++arg="$1" ++case "$arg" in ++ "-l") # files included in initramfs - used by kbuild ++ dep_list="list_" ++ echo "deps_initramfs := $0 \\" ++ shift ++ ;; ++ "-o") # generate compressed cpio image named $1 ++ shift ++ output_file="$1" ++ cpio_list="$(mktemp ${TMPDIR:-/tmp}/cpiolist.XXXXXX)" ++ output=${cpio_list} ++ echo "$output_file" | grep -q "\.gz$" \ ++ && [ -x "`which gzip 2> /dev/null`" ] \ ++ && compr="gzip -n -9 -f" ++ echo "$output_file" | grep -q "\.bz2$" \ ++ && [ -x "`which bzip2 2> /dev/null`" ] \ ++ && compr="bzip2 -9 -f" ++ echo "$output_file" | grep -q "\.lzma$" \ ++ && [ -x "`which lzma 2> /dev/null`" ] \ ++ && compr="lzma -9 -f" ++ echo "$output_file" | grep -q "\.xz$" \ ++ && [ -x "`which xz 2> /dev/null`" ] \ ++ && compr="xz --check=crc32 --lzma2=dict=1MiB" ++ echo "$output_file" | grep -q "\.lzo$" \ ++ && [ -x "`which lzop 2> /dev/null`" ] \ ++ && compr="lzop -9 -f" ++ echo "$output_file" | grep -q "\.lz4$" \ ++ && [ -x "`which lz4 2> /dev/null`" ] \ ++ && compr="lz4 -l -9 -f" ++ echo "$output_file" | grep -q "\.cpio$" && compr="cat" ++ shift ++ ;; ++esac ++while [ $# -gt 0 ]; do ++ arg="$1" ++ shift ++ case "$arg" in ++ "-u") # map $1 to uid=0 (root) ++ root_uid="$1" ++ [ "$root_uid" = "-1" ] && root_uid=$(id -u || echo 0) ++ shift ++ ;; ++ "-g") # map $1 to gid=0 (root) ++ root_gid="$1" ++ [ "$root_gid" = "-1" ] && root_gid=$(id -g || echo 0) ++ shift ++ ;; ++ "-d") # display default initramfs list ++ default_list="$arg" ++ ${dep_list}default_initramfs ++ ;; ++ "-h") ++ usage ++ exit 0 ++ ;; ++ *) ++ case "$arg" in ++ "-"*) ++ unknown_option ++ ;; ++ *) # input file/dir - process it ++ input_file "$arg" "$#" ++ ;; ++ esac ++ ;; ++ esac ++done ++ ++# If output_file is set we will generate cpio archive and compress it ++# we are careful to delete tmp files ++if [ ! -z ${output_file} ]; then ++ if [ -z ${cpio_file} ]; then ++ timestamp= ++ if test -n "$KBUILD_BUILD_TIMESTAMP"; then ++ timestamp="$(date -d"$KBUILD_BUILD_TIMESTAMP" +%s || :)" ++ if test -n "$timestamp"; then ++ timestamp="-t $timestamp" ++ fi ++ fi ++ cpio_tfile="$(mktemp ${TMPDIR:-/tmp}/cpiofile.XXXXXX)" ++ usr/gen_init_cpio $timestamp ${cpio_list} > ${cpio_tfile} ++ else ++ cpio_tfile=${cpio_file} ++ fi ++ rm ${cpio_list} ++ if [ "${is_cpio_compressed}" = "compressed" ]; then ++ cat ${cpio_tfile} > ${output_file} ++ else ++ (cat ${cpio_tfile} | ${compr} - > ${output_file}) \ ++ || (rm -f ${output_file} ; false) ++ fi ++ [ -z ${cpio_file} ] && rm ${cpio_tfile} ++fi ++exit 0 diff --git a/target/linux/starfive/patches-6.6/0062-i2c-designware-Delete-SMBus-functionalities.patch b/target/linux/starfive/patches-6.6/0062-i2c-designware-Delete-SMBus-functionalities.patch new file mode 100644 index 0000000000..05561f1c51 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0062-i2c-designware-Delete-SMBus-functionalities.patch @@ -0,0 +1,33 @@ +From dcc2827ed6e701a65731c05b0297745559837217 Mon Sep 17 00:00:00 2001 +From: Hal Feng +Date: Fri, 12 May 2023 17:33:20 +0800 +Subject: [PATCH 062/116] i2c: designware: Delete SMBus functionalities + +The driver didn't implement the smbus interface, +so replace the SMBus functionalities with +I2C_FUNC_SMBUS_EMUL. + +Signed-off-by: Hal Feng +--- + drivers/i2c/busses/i2c-designware-core.h | 10 ++++------ + 1 file changed, 4 insertions(+), 6 deletions(-) + +--- a/drivers/i2c/busses/i2c-designware-core.h ++++ b/drivers/i2c/busses/i2c-designware-core.h +@@ -18,12 +18,10 @@ + #include + #include + +-#define DW_IC_DEFAULT_FUNCTIONALITY (I2C_FUNC_I2C | \ +- I2C_FUNC_SMBUS_BYTE | \ +- I2C_FUNC_SMBUS_BYTE_DATA | \ +- I2C_FUNC_SMBUS_WORD_DATA | \ +- I2C_FUNC_SMBUS_BLOCK_DATA | \ +- I2C_FUNC_SMBUS_I2C_BLOCK) ++#define DW_IC_DEFAULT_FUNCTIONALITY (I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL \ ++ & ~I2C_FUNC_SMBUS_QUICK \ ++ & ~I2C_FUNC_SMBUS_PROC_CALL \ ++ & ~I2C_FUNC_SMBUS_PEC)) + + #define DW_IC_CON_MASTER BIT(0) + #define DW_IC_CON_SPEED_STD (1 << 1) diff --git a/target/linux/starfive/patches-6.6/0063-drivers-mtd-gigadevice-add-gd25lq256d-32M-flash-supp.patch b/target/linux/starfive/patches-6.6/0063-drivers-mtd-gigadevice-add-gd25lq256d-32M-flash-supp.patch new file mode 100644 index 0000000000..43a7e5ba0d --- /dev/null +++ b/target/linux/starfive/patches-6.6/0063-drivers-mtd-gigadevice-add-gd25lq256d-32M-flash-supp.patch @@ -0,0 +1,26 @@ +From b61cefc6c785aa8a7177a0b535db746fd0047bd8 Mon Sep 17 00:00:00 2001 +From: Ziv Xu +Date: Fri, 19 Jan 2024 15:22:55 +0800 +Subject: [PATCH 063/116] drivers: mtd: gigadevice: add gd25lq256d 32M flash + support + +add gd25lq256d 32M flash support + +Signed-off-by: Ziv Xu +--- + drivers/mtd/spi-nor/gigadevice.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/drivers/mtd/spi-nor/gigadevice.c ++++ b/drivers/mtd/spi-nor/gigadevice.c +@@ -66,6 +66,10 @@ static const struct flash_info gigadevic + FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) + NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | + SPI_NOR_QUAD_READ) }, ++ { "gd25lq256d", INFO(0xc86019, 0, 64 * 1024, 512) ++ FLAGS( SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_QUAD_PP) ++ NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | ++ SPI_NOR_QUAD_READ) }, + { "gd25q256", INFO(0xc84019, 0, 64 * 1024, 512) + PARSE_SFDP + FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6) diff --git a/target/linux/starfive/patches-6.6/0064-driver-mailbox-Add-mailbox-driver.patch b/target/linux/starfive/patches-6.6/0064-driver-mailbox-Add-mailbox-driver.patch new file mode 100644 index 0000000000..b733110f7d --- /dev/null +++ b/target/linux/starfive/patches-6.6/0064-driver-mailbox-Add-mailbox-driver.patch @@ -0,0 +1,808 @@ +From 76bc13aa12bd111f5da01e107f8d487b20b5a40c Mon Sep 17 00:00:00 2001 +From: "shanlong.li" +Date: Thu, 8 Jun 2023 00:07:15 -0700 +Subject: [PATCH 064/116] driver: mailbox: Add mailbox driver + +Add mailbox driver. + +Signed-off-by: shanlong.li +Signed-off-by: Hal Feng +--- + drivers/mailbox/Kconfig | 13 + + drivers/mailbox/Makefile | 4 + + drivers/mailbox/starfive_mailbox-test.c | 407 ++++++++++++++++++++++++ + drivers/mailbox/starfive_mailbox.c | 347 ++++++++++++++++++++ + 4 files changed, 771 insertions(+) + create mode 100644 drivers/mailbox/starfive_mailbox-test.c + create mode 100644 drivers/mailbox/starfive_mailbox.c + +--- a/drivers/mailbox/Kconfig ++++ b/drivers/mailbox/Kconfig +@@ -295,4 +295,17 @@ config QCOM_IPCC + acts as an interrupt controller for receiving interrupts from clients. + Say Y here if you want to build this driver. + ++config STARFIVE_MBOX ++ tristate "Platform Starfive Mailbox" ++ depends on OF ++ help ++ Say Y here if you want to build a platform specific variant RISCV ++ controller driver. ++ ++config STARFIVE_MBOX_TEST ++ tristate "Starfive Mailbox Test Client" ++ depends on OF ++ depends on HAS_IOMEM ++ help ++ Test client to help with testing new Controller driver implementations. + endif +--- a/drivers/mailbox/Makefile ++++ b/drivers/mailbox/Makefile +@@ -62,3 +62,7 @@ obj-$(CONFIG_SPRD_MBOX) += sprd-mailbox + obj-$(CONFIG_QCOM_IPCC) += qcom-ipcc.o + + obj-$(CONFIG_APPLE_MAILBOX) += apple-mailbox.o ++ ++obj-$(CONFIG_STARFIVE_MBOX) += starfive_mailbox.o ++ ++obj-$(CONFIG_STARFIVE_MBOX_TEST) += starfive_mailbox-test.o +--- /dev/null ++++ b/drivers/mailbox/starfive_mailbox-test.c +@@ -0,0 +1,407 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * Copyright (C) 2015 ST Microelectronics ++ * ++ * Author: Lee Jones ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#define MBOX_MAX_SIG_LEN 8 ++#define MBOX_MAX_MSG_LEN 16 ++#define MBOX_BYTES_PER_LINE 16 ++#define MBOX_HEXDUMP_LINE_LEN ((MBOX_BYTES_PER_LINE * 4) + 2) ++#define MBOX_HEXDUMP_MAX_LEN (MBOX_HEXDUMP_LINE_LEN * (MBOX_MAX_MSG_LEN / MBOX_BYTES_PER_LINE)) ++ ++static bool mbox_data_ready; ++ ++struct mbox_test_device { ++ struct device *dev; ++ void __iomem *tx_mmio; ++ void __iomem *rx_mmio; ++ struct mbox_chan *tx_channel; ++ struct mbox_chan *rx_channel; ++ char *rx_buffer; ++ char *signal; ++ char *message; ++ spinlock_t lock; ++ wait_queue_head_t waitq; ++ struct fasync_struct *async_queue; ++ struct dentry *root_debugfs_dir; ++}; ++ ++static ssize_t mbox_test_signal_write(struct file *filp, ++ const char __user *userbuf, ++ size_t count, loff_t *ppos) ++{ ++ struct mbox_test_device *tdev = filp->private_data; ++ ++ if (!tdev->tx_channel) { ++ dev_err(tdev->dev, "Channel cannot do Tx\n"); ++ return -EINVAL; ++ } ++ ++ if (count > MBOX_MAX_SIG_LEN) { ++ dev_err(tdev->dev, ++ "Signal length %zd greater than max allowed %d\n", ++ count, MBOX_MAX_SIG_LEN); ++ return -EINVAL; ++ } ++ ++ /* Only allocate memory if we need to */ ++ if (!tdev->signal) { ++ tdev->signal = kzalloc(MBOX_MAX_SIG_LEN, GFP_KERNEL); ++ if (!tdev->signal) ++ return -ENOMEM; ++ } ++ ++ if (copy_from_user(tdev->signal, userbuf, count)) { ++ kfree(tdev->signal); ++ tdev->signal = NULL; ++ return -EFAULT; ++ } ++ ++ return count; ++} ++ ++static const struct file_operations mbox_test_signal_ops = { ++ .write = mbox_test_signal_write, ++ .open = simple_open, ++ .llseek = generic_file_llseek, ++}; ++ ++static int mbox_test_message_fasync(int fd, struct file *filp, int on) ++{ ++ struct mbox_test_device *tdev = filp->private_data; ++ ++ return fasync_helper(fd, filp, on, &tdev->async_queue); ++} ++ ++static ssize_t mbox_test_message_write(struct file *filp, ++ const char __user *userbuf, ++ size_t count, loff_t *ppos) ++{ ++ struct mbox_test_device *tdev = filp->private_data; ++ void *data; ++ int ret; ++ ++ if (!tdev->tx_channel) { ++ dev_err(tdev->dev, "Channel cannot do Tx\n"); ++ return -EINVAL; ++ } ++ ++ if (count > MBOX_MAX_MSG_LEN) { ++ dev_err(tdev->dev, ++ "Message length %zd greater than max allowed %d\n", ++ count, MBOX_MAX_MSG_LEN); ++ return -EINVAL; ++ } ++ ++ tdev->message = kzalloc(MBOX_MAX_MSG_LEN, GFP_KERNEL); ++ if (!tdev->message) ++ return -ENOMEM; ++ ++ ret = copy_from_user(tdev->message, userbuf, count); ++ if (ret) { ++ ret = -EFAULT; ++ goto out; ++ } ++ ++ if (tdev->tx_mmio && tdev->signal) { ++ print_hex_dump_bytes("Client: Sending: Signal: ", DUMP_PREFIX_ADDRESS, ++ tdev->signal, MBOX_MAX_SIG_LEN); ++ ++ data = tdev->signal; ++ } else ++ data = tdev->message; ++ ++ print_hex_dump_bytes("Client: Sending: Message: ", DUMP_PREFIX_ADDRESS, ++ tdev->message, MBOX_MAX_MSG_LEN); ++ ++ ret = mbox_send_message(tdev->tx_channel, data); ++ mbox_chan_txdone(tdev->tx_channel, ret); ++ if (ret < 0) ++ dev_err(tdev->dev, "Failed to send message via mailbox\n"); ++ ++out: ++ kfree(tdev->signal); ++ kfree(tdev->message); ++ tdev->signal = NULL; ++ ++ return ret < 0 ? ret : count; ++} ++ ++static bool mbox_test_message_data_ready(struct mbox_test_device *tdev) ++{ ++ bool data_ready; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&tdev->lock, flags); ++ data_ready = mbox_data_ready; ++ spin_unlock_irqrestore(&tdev->lock, flags); ++ ++ return data_ready; ++} ++ ++static ssize_t mbox_test_message_read(struct file *filp, char __user *userbuf, ++ size_t count, loff_t *ppos) ++{ ++ struct mbox_test_device *tdev = filp->private_data; ++ unsigned long flags; ++ char *touser, *ptr; ++ int ret; ++ ++ touser = kzalloc(MBOX_HEXDUMP_MAX_LEN + 1, GFP_KERNEL); ++ if (!touser) ++ return -ENOMEM; ++ ++ if (!tdev->rx_channel) { ++ ret = snprintf(touser, 20, "\n"); ++ ret = simple_read_from_buffer(userbuf, count, ppos, ++ touser, ret); ++ goto kfree_err; ++ } ++ ++ do { ++ if (mbox_test_message_data_ready(tdev)) ++ break; ++ ++ if (filp->f_flags & O_NONBLOCK) { ++ ret = -EAGAIN; ++ goto waitq_err; ++ } ++ ++ if (signal_pending(current)) { ++ ret = -ERESTARTSYS; ++ goto waitq_err; ++ } ++ schedule(); ++ ++ } while (1); ++ ++ spin_lock_irqsave(&tdev->lock, flags); ++ ++ ptr = tdev->rx_buffer; ++ ++ mbox_data_ready = false; ++ ++ spin_unlock_irqrestore(&tdev->lock, flags); ++ if (copy_to_user((void __user *)userbuf, ptr, 4)) ++ ret = -EFAULT; ++ ++waitq_err: ++ __set_current_state(TASK_RUNNING); ++kfree_err: ++ kfree(touser); ++ return ret; ++} ++ ++static __poll_t ++mbox_test_message_poll(struct file *filp, struct poll_table_struct *wait) ++{ ++ struct mbox_test_device *tdev = filp->private_data; ++ ++ poll_wait(filp, &tdev->waitq, wait); ++ ++ if (mbox_test_message_data_ready(tdev)) ++ return EPOLLIN | EPOLLRDNORM; ++ return 0; ++} ++ ++static const struct file_operations mbox_test_message_ops = { ++ .write = mbox_test_message_write, ++ .read = mbox_test_message_read, ++ .fasync = mbox_test_message_fasync, ++ .poll = mbox_test_message_poll, ++ .open = simple_open, ++ .llseek = generic_file_llseek, ++}; ++ ++static int mbox_test_add_debugfs(struct platform_device *pdev, ++ struct mbox_test_device *tdev) ++{ ++ if (!debugfs_initialized()) ++ return 0; ++ ++ tdev->root_debugfs_dir = debugfs_create_dir(dev_name(&pdev->dev), NULL); ++ if (!tdev->root_debugfs_dir) { ++ dev_err(&pdev->dev, "Failed to create Mailbox debugfs\n"); ++ return -EINVAL; ++ } ++ ++ debugfs_create_file("message", 0600, tdev->root_debugfs_dir, ++ tdev, &mbox_test_message_ops); ++ ++ debugfs_create_file("signal", 0200, tdev->root_debugfs_dir, ++ tdev, &mbox_test_signal_ops); ++ ++ return 0; ++} ++ ++static void mbox_test_receive_message(struct mbox_client *client, void *message) ++{ ++ struct mbox_test_device *tdev = dev_get_drvdata(client->dev); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&tdev->lock, flags); ++ if (tdev->rx_mmio) { ++ memcpy_fromio(tdev->rx_buffer, tdev->rx_mmio, MBOX_MAX_MSG_LEN); ++ print_hex_dump_bytes("Client: Received [MMIO]: ", DUMP_PREFIX_ADDRESS, ++ tdev->rx_buffer, MBOX_MAX_MSG_LEN); ++ } else if (message) { ++ print_hex_dump_bytes("Client: Received [API]: ", DUMP_PREFIX_ADDRESS, ++ message, MBOX_MAX_MSG_LEN); ++ memcpy(tdev->rx_buffer, message, MBOX_MAX_MSG_LEN); ++ } ++ mbox_data_ready = true; ++ spin_unlock_irqrestore(&tdev->lock, flags); ++} ++ ++static void mbox_test_prepare_message(struct mbox_client *client, void *message) ++{ ++ struct mbox_test_device *tdev = dev_get_drvdata(client->dev); ++ ++ if (tdev->tx_mmio) { ++ if (tdev->signal) ++ memcpy_toio(tdev->tx_mmio, tdev->message, MBOX_MAX_MSG_LEN); ++ else ++ memcpy_toio(tdev->tx_mmio, message, MBOX_MAX_MSG_LEN); ++ } ++} ++ ++static struct mbox_chan * ++mbox_test_request_channel(struct platform_device *pdev, const char *name) ++{ ++ struct mbox_client *client; ++ struct mbox_chan *channel; ++ ++ client = devm_kzalloc(&pdev->dev, sizeof(*client), GFP_KERNEL); ++ if (!client) ++ return ERR_PTR(-ENOMEM); ++ ++ client->dev = &pdev->dev; ++ client->rx_callback = mbox_test_receive_message; ++ client->tx_prepare = mbox_test_prepare_message; ++ client->tx_block = false; ++ client->knows_txdone = false; ++ client->tx_tout = 500; ++ ++ channel = mbox_request_channel_byname(client, name); ++ if (IS_ERR(channel)) { ++ dev_warn(&pdev->dev, "Failed to request %s channel\n", name); ++ return NULL; ++ } ++ ++ return channel; ++} ++ ++static int mbox_test_probe(struct platform_device *pdev) ++{ ++ struct mbox_test_device *tdev; ++ struct resource *res; ++ resource_size_t size; ++ int ret; ++ ++ tdev = devm_kzalloc(&pdev->dev, sizeof(*tdev), GFP_KERNEL); ++ if (!tdev) ++ return -ENOMEM; ++ ++ /* It's okay for MMIO to be NULL */ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ tdev->tx_mmio = devm_ioremap_resource(&pdev->dev, res); ++ if (PTR_ERR(tdev->tx_mmio) == -EBUSY) { ++ /* if reserved area in SRAM, try just ioremap */ ++ size = resource_size(res); ++ tdev->tx_mmio = devm_ioremap(&pdev->dev, res->start, size); ++ } else if (IS_ERR(tdev->tx_mmio)) { ++ tdev->tx_mmio = NULL; ++ } ++ ++ /* If specified, second reg entry is Rx MMIO */ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 1); ++ tdev->rx_mmio = devm_ioremap_resource(&pdev->dev, res); ++ if (PTR_ERR(tdev->rx_mmio) == -EBUSY) { ++ size = resource_size(res); ++ tdev->rx_mmio = devm_ioremap(&pdev->dev, res->start, size); ++ } else if (IS_ERR(tdev->rx_mmio)) { ++ tdev->rx_mmio = tdev->tx_mmio; ++ } ++ ++ tdev->tx_channel = mbox_test_request_channel(pdev, "tx"); ++ tdev->rx_channel = mbox_test_request_channel(pdev, "rx"); ++ ++ if (!tdev->tx_channel && !tdev->rx_channel) ++ return -EPROBE_DEFER; ++ ++ /* If Rx is not specified but has Rx MMIO, then Rx = Tx */ ++ if (!tdev->rx_channel && (tdev->rx_mmio != tdev->tx_mmio)) ++ tdev->rx_channel = tdev->tx_channel; ++ ++ tdev->dev = &pdev->dev; ++ platform_set_drvdata(pdev, tdev); ++ ++ spin_lock_init(&tdev->lock); ++ ++ if (tdev->rx_channel) { ++ tdev->rx_buffer = devm_kzalloc(&pdev->dev, ++ MBOX_MAX_MSG_LEN, GFP_KERNEL); ++ if (!tdev->rx_buffer) ++ return -ENOMEM; ++ } ++ ++ ret = mbox_test_add_debugfs(pdev, tdev); ++ if (ret) ++ return ret; ++ ++ dev_info(&pdev->dev, "Successfully registered\n"); ++ ++ return 0; ++} ++ ++static int mbox_test_remove(struct platform_device *pdev) ++{ ++ struct mbox_test_device *tdev = platform_get_drvdata(pdev); ++ ++ debugfs_remove_recursive(tdev->root_debugfs_dir); ++ ++ if (tdev->tx_channel) ++ mbox_free_channel(tdev->tx_channel); ++ if (tdev->rx_channel) ++ mbox_free_channel(tdev->rx_channel); ++ ++ return 0; ++} ++ ++static const struct of_device_id mbox_test_match[] = { ++ { .compatible = "starfive,mailbox-test" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, mbox_test_match); ++ ++static struct platform_driver mbox_test_driver = { ++ .driver = { ++ .name = "mailbox_test", ++ .of_match_table = mbox_test_match, ++ }, ++ .probe = mbox_test_probe, ++ .remove = mbox_test_remove, ++}; ++module_platform_driver(mbox_test_driver); ++ ++MODULE_DESCRIPTION("Generic Mailbox Testing Facility"); ++MODULE_AUTHOR("Lee Jones ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "mailbox.h" ++ ++#define MBOX_CHAN_MAX 4 ++ ++#define MBOX_BASE(mbox, ch) ((mbox)->base + ((ch) * 0x10)) ++#define MBOX_IRQ_REG 0x00 ++#define MBOX_SET_REG 0x04 ++#define MBOX_CLR_REG 0x08 ++#define MBOX_CMD_REG 0x0c ++#define MBC_PEND_SMRY 0x100 ++ ++typedef enum { ++ MAILBOX_CORE_U7 = 0, ++ MAILBOX_CORE_HIFI4, ++ MAILBOX_CORE_E2, ++ MAILBOX_CORE_RSVD0, ++ MAILBOX_CORE_NUM, ++} mailbox_core_t; ++ ++struct mailbox_irq_name_c{ ++ int id; ++ char name[16]; ++}; ++ ++static const struct mailbox_irq_name_c irq_peer_name[MBOX_CHAN_MAX] = { ++ {MAILBOX_CORE_U7, "u74_core"}, ++ {MAILBOX_CORE_HIFI4, "hifi4_core"}, ++ {MAILBOX_CORE_E2, "e24_core"}, ++ {MAILBOX_CORE_RSVD0, "" }, ++}; ++ ++/** ++ * starfive mailbox channel information ++ * ++ * A channel can be used for TX or RX, it can trigger remote ++ * processor interrupt to notify remote processor and can receive ++ * interrupt if has incoming message. ++ * ++ * @dst_irq: Interrupt vector for remote processor ++ * @core_id: id for remote processor ++ */ ++struct starfive_chan_info { ++ unsigned int dst_irq; ++ mailbox_core_t core_id; ++}; ++ ++/** ++ * starfive mailbox controller data ++ * ++ * Mailbox controller includes 4 channels and can allocate ++ * channel for message transferring. ++ * ++ * @dev: Device to which it is attached ++ * @base: Base address of the register mapping region ++ * @chan: Representation of channels in mailbox controller ++ * @mchan: Representation of channel info ++ * @controller: Representation of a communication channel controller ++ */ ++struct starfive_mbox { ++ struct device *dev; ++ void __iomem *base; ++ struct mbox_chan chan[MBOX_CHAN_MAX]; ++ struct starfive_chan_info mchan[MBOX_CHAN_MAX]; ++ struct mbox_controller controller; ++ struct clk *clk; ++ struct reset_control *rst_rresetn; ++}; ++ ++static struct starfive_mbox *to_starfive_mbox(struct mbox_controller *mbox) ++{ ++ return container_of(mbox, struct starfive_mbox, controller); ++} ++ ++static struct mbox_chan * ++starfive_of_mbox_index_xlate(struct mbox_controller *mbox, ++ const struct of_phandle_args *sp) ++{ ++ struct starfive_mbox *sbox; ++ ++ int ind = sp->args[0]; ++ int core_id = sp->args[1]; ++ ++ if (ind >= mbox->num_chans || core_id >= MAILBOX_CORE_NUM) ++ return ERR_PTR(-EINVAL); ++ ++ sbox = to_starfive_mbox(mbox); ++ ++ sbox->mchan[ind].core_id = core_id; ++ ++ return &mbox->chans[ind]; ++} ++ ++static irqreturn_t starfive_rx_irq_handler(int irq, void *p) ++{ ++ struct mbox_chan *chan = p; ++ unsigned long ch = (unsigned long)chan->con_priv; ++ struct starfive_mbox *mbox = to_starfive_mbox(chan->mbox); ++ void __iomem *base = MBOX_BASE(mbox, ch); ++ u32 val; ++ ++ val = readl(base + MBOX_CMD_REG); ++ if (!val) ++ return IRQ_NONE; ++ ++ mbox_chan_received_data(chan, (void *)&val); ++ writel(val, base + MBOX_CLR_REG); ++ return IRQ_HANDLED; ++} ++ ++static int starfive_mbox_check_state(struct mbox_chan *chan) ++{ ++ unsigned long ch = (unsigned long)chan->con_priv; ++ struct starfive_mbox *mbox = to_starfive_mbox(chan->mbox); ++ unsigned long irq_flag = IRQF_SHARED; ++ long ret = 0; ++ ++ pm_runtime_get_sync(mbox->dev); ++ /* MAILBOX should be with IRQF_NO_SUSPEND set */ ++ if (!mbox->dev->pm_domain) ++ irq_flag |= IRQF_NO_SUSPEND; ++ ++ /* Mailbox is idle so directly bail out */ ++ if (readl(mbox->base + MBC_PEND_SMRY) & BIT(ch)) ++ return -EBUSY; ++ ++ if (mbox->mchan[ch].dst_irq > 0) { ++ dev_dbg(mbox->dev, "%s: host IRQ = %d, ch:%ld", __func__, mbox->mchan[ch].dst_irq, ch); ++ ret = devm_request_irq(mbox->dev, mbox->mchan[ch].dst_irq, starfive_rx_irq_handler, ++ irq_flag, irq_peer_name[ch].name, chan); ++ if (ret < 0) ++ dev_err(mbox->dev, "request_irq %d failed\n", mbox->mchan[ch].dst_irq); ++ } ++ ++ return ret; ++} ++ ++static int starfive_mbox_startup(struct mbox_chan *chan) ++{ ++ return starfive_mbox_check_state(chan); ++} ++ ++static void starfive_mbox_shutdown(struct mbox_chan *chan) ++{ ++ struct starfive_mbox *mbox = to_starfive_mbox(chan->mbox); ++ unsigned long ch = (unsigned long)chan->con_priv; ++ void __iomem *base = MBOX_BASE(mbox, ch); ++ ++ writel(0x0, base + MBOX_IRQ_REG); ++ writel(0x0, base + MBOX_CLR_REG); ++ ++ if (mbox->mchan[ch].dst_irq > 0) ++ devm_free_irq(mbox->dev, mbox->mchan[ch].dst_irq, chan); ++ pm_runtime_put_sync(mbox->dev); ++} ++ ++static int starfive_mbox_send_data(struct mbox_chan *chan, void *msg) ++{ ++ unsigned long ch = (unsigned long)chan->con_priv; ++ struct starfive_mbox *mbox = to_starfive_mbox(chan->mbox); ++ struct starfive_chan_info *mchan = &mbox->mchan[ch]; ++ void __iomem *base = MBOX_BASE(mbox, ch); ++ u32 *buf = msg; ++ ++ /* Ensure channel is released */ ++ if (readl(mbox->base + MBC_PEND_SMRY) & BIT(ch)) { ++ pr_debug("%s:%d. busy\n", __func__, __LINE__); ++ return -EBUSY; ++ } ++ ++ /* Clear mask for destination interrupt */ ++ writel(BIT(mchan->core_id), base + MBOX_IRQ_REG); ++ ++ /* Fill message data */ ++ writel(*buf, base + MBOX_SET_REG); ++ return 0; ++} ++ ++static struct mbox_chan_ops starfive_mbox_ops = { ++ .startup = starfive_mbox_startup, ++ .send_data = starfive_mbox_send_data, ++ .shutdown = starfive_mbox_shutdown, ++}; ++ ++static const struct of_device_id starfive_mbox_of_match[] = { ++ { .compatible = "starfive,mail_box",}, ++ {}, ++}; ++ ++MODULE_DEVICE_TABLE(of, starfive_mbox_of_match); ++ ++void starfive_mailbox_init(struct starfive_mbox *mbox) ++{ ++ mbox->clk = devm_clk_get_optional(mbox->dev, "clk_apb"); ++ if (IS_ERR(mbox->clk)) { ++ dev_err(mbox->dev, "failed to get mailbox\n"); ++ return; ++ } ++ ++ mbox->rst_rresetn = devm_reset_control_get_exclusive(mbox->dev, "mbx_rre"); ++ if (IS_ERR(mbox->rst_rresetn)) { ++ dev_err(mbox->dev, "failed to get mailbox reset\n"); ++ return; ++ } ++ ++ clk_prepare_enable(mbox->clk); ++ reset_control_deassert(mbox->rst_rresetn); ++} ++ ++static int starfive_mbox_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct starfive_mbox *mbox; ++ struct mbox_chan *chan; ++ struct resource *res; ++ unsigned long ch; ++ int err; ++ ++ mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL); ++ if (!mbox) ++ return -ENOMEM; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ mbox->base = devm_ioremap_resource(dev, res); ++ mbox->dev = dev; ++ ++ if (IS_ERR(mbox->base)) ++ return PTR_ERR(mbox->base); ++ ++ starfive_mailbox_init(mbox); ++ ++ mbox->controller.dev = dev; ++ mbox->controller.chans = mbox->chan; ++ mbox->controller.num_chans = MBOX_CHAN_MAX; ++ mbox->controller.ops = &starfive_mbox_ops; ++ mbox->controller.of_xlate = starfive_of_mbox_index_xlate; ++ mbox->controller.txdone_irq = true; ++ mbox->controller.txdone_poll = false; ++ ++ /* Initialize mailbox channel data */ ++ chan = mbox->chan; ++ for (ch = 0; ch < MBOX_CHAN_MAX; ch++) { ++ mbox->mchan[ch].dst_irq = 0; ++ mbox->mchan[ch].core_id = (mailbox_core_t)ch; ++ chan[ch].con_priv = (void *)ch; ++ } ++ mbox->mchan[MAILBOX_CORE_HIFI4].dst_irq = platform_get_irq(pdev, 0); ++ mbox->mchan[MAILBOX_CORE_E2].dst_irq = platform_get_irq(pdev, 1); ++ ++ err = mbox_controller_register(&mbox->controller); ++ if (err) { ++ dev_err(dev, "Failed to register mailbox %d\n", err); ++ return err; ++ } ++ ++ platform_set_drvdata(pdev, mbox); ++ dev_info(dev, "Mailbox enabled\n"); ++ pm_runtime_set_active(dev); ++ pm_runtime_enable(dev); ++ ++ return 0; ++} ++ ++static int starfive_mbox_remove(struct platform_device *pdev) ++{ ++ struct starfive_mbox *mbox = platform_get_drvdata(pdev); ++ ++ mbox_controller_unregister(&mbox->controller); ++ devm_clk_put(mbox->dev, mbox->clk); ++ pm_runtime_disable(mbox->dev); ++ ++ return 0; ++} ++ ++static int __maybe_unused starfive_mbox_suspend(struct device *dev) ++{ ++ struct starfive_mbox *mbox = dev_get_drvdata(dev); ++ ++ clk_disable_unprepare(mbox->clk); ++ ++ return 0; ++} ++ ++static int __maybe_unused starfive_mbox_resume(struct device *dev) ++{ ++ struct starfive_mbox *mbox = dev_get_drvdata(dev); ++ int ret; ++ ++ ret = clk_prepare_enable(mbox->clk); ++ if (ret) ++ dev_err(dev, "failed to enable clock\n"); ++ ++ return ret; ++} ++ ++static const struct dev_pm_ops starfive_mbox_pm_ops = { ++ .suspend = starfive_mbox_suspend, ++ .resume = starfive_mbox_resume, ++ SET_RUNTIME_PM_OPS(starfive_mbox_suspend, starfive_mbox_resume, NULL) ++}; ++static struct platform_driver starfive_mbox_driver = { ++ .probe = starfive_mbox_probe, ++ .remove = starfive_mbox_remove, ++ .driver = { ++ .name = "mailbox", ++ .of_match_table = starfive_mbox_of_match, ++ .pm = &starfive_mbox_pm_ops, ++ }, ++}; ++ ++static int __init starfive_mbox_init(void) ++{ ++ return platform_driver_register(&starfive_mbox_driver); ++} ++core_initcall(starfive_mbox_init); ++ ++static void __exit starfive_mbox_exit(void) ++{ ++ platform_driver_unregister(&starfive_mbox_driver); ++} ++module_exit(starfive_mbox_exit); ++ ++MODULE_DESCRIPTION("StarFive Mailbox Controller driver"); ++MODULE_AUTHOR("Shanlong Li "); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/starfive/patches-6.6/0065-driver-rtc-Add-StarFive-JH7110-rtc-driver.patch b/target/linux/starfive/patches-6.6/0065-driver-rtc-Add-StarFive-JH7110-rtc-driver.patch new file mode 100644 index 0000000000..0257cb004b --- /dev/null +++ b/target/linux/starfive/patches-6.6/0065-driver-rtc-Add-StarFive-JH7110-rtc-driver.patch @@ -0,0 +1,789 @@ +From 0f44bd6bec708782f38bba4d03deecf927d1c83d Mon Sep 17 00:00:00 2001 +From: "ziv.xu" +Date: Fri, 9 Jun 2023 15:31:53 +0800 +Subject: [PATCH 065/116] driver: rtc: Add StarFive JH7110 rtc driver + +Add RTC driver and support for StarFive JH7110 SoC. + +Signed-off-by: ziv.xu +Signed-off-by: Hal Feng +--- + drivers/rtc/Kconfig | 8 + + drivers/rtc/Makefile | 1 + + drivers/rtc/rtc-starfive.c | 743 +++++++++++++++++++++++++++++++++++++ + 3 files changed, 752 insertions(+) + create mode 100644 drivers/rtc/rtc-starfive.c + +--- a/drivers/rtc/Kconfig ++++ b/drivers/rtc/Kconfig +@@ -1327,6 +1327,14 @@ config RTC_DRV_NTXEC + embedded controller found in certain e-book readers designed by the + original design manufacturer Netronix. + ++config RTC_DRV_STARFIVE ++ tristate "StarFive 32.768k-RTC" ++ depends on ARCH_STARFIVE ++ depends on OF ++ help ++ If you say Y here you will get support for the RTC found on ++ StarFive SOCS. ++ + comment "on-CPU RTC drivers" + + config RTC_DRV_ASM9260 +--- a/drivers/rtc/Makefile ++++ b/drivers/rtc/Makefile +@@ -163,6 +163,7 @@ obj-$(CONFIG_RTC_DRV_SH) += rtc-sh.o + obj-$(CONFIG_RTC_DRV_SNVS) += rtc-snvs.o + obj-$(CONFIG_RTC_DRV_SPEAR) += rtc-spear.o + obj-$(CONFIG_RTC_DRV_STARFIRE) += rtc-starfire.o ++obj-$(CONFIG_RTC_DRV_STARFIVE) += rtc-starfive.o + obj-$(CONFIG_RTC_DRV_STK17TA8) += rtc-stk17ta8.o + obj-$(CONFIG_RTC_DRV_ST_LPC) += rtc-st-lpc.o + obj-$(CONFIG_RTC_DRV_STM32) += rtc-stm32.o +--- /dev/null ++++ b/drivers/rtc/rtc-starfive.c +@@ -0,0 +1,743 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * RTC driver for the StarFive JH7110 SoC ++ * ++ * Copyright (C) 2021 StarFive Technology Co., Ltd. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* Registers */ ++#define SFT_RTC_CFG 0x00 ++#define SFT_RTC_SW_CAL_VALUE 0x04 ++#define SFT_RTC_HW_CAL_CFG 0x08 ++#define SFT_RTC_CMP_CFG 0x0C ++#define SFT_RTC_IRQ_EN 0x10 ++#define SFT_RTC_IRQ_EVEVT 0x14 ++#define SFT_RTC_IRQ_STATUS 0x18 ++#define SFT_RTC_CAL_VALUE 0x24 ++#define SFT_RTC_CFG_TIME 0x28 ++#define SFT_RTC_CFG_DATE 0x2C ++#define SFT_RTC_ACT_TIME 0x34 ++#define SFT_RTC_ACT_DATE 0x38 ++#define SFT_RTC_TIME 0x3C ++#define SFT_RTC_DATE 0x40 ++#define SFT_RTC_TIME_LATCH 0x44 ++#define SFT_RTC_DATE_LATCH 0x48 ++ ++/* RTC_CFG */ ++#define RTC_CFG_ENABLE_SHIFT 0 /* RW: RTC Enable. */ ++#define RTC_CFG_CAL_EN_HW_SHIFT 1 /* RW: Enable of hardware calibretion. */ ++#define RTC_CFG_CAL_SEL_SHIFT 2 /* RW: select the hw/sw calibretion mode.*/ ++#define RTC_CFG_HOUR_MODE_SHIFT 3 /* RW: time hour mode. 24h|12h */ ++ ++/* RTC_SW_CAL_VALUE */ ++#define RTC_SW_CAL_VALUE_MASK GENMASK(15, 0) ++#define RTC_SW_CAL_MAX RTC_SW_CAL_VALUE_MASK ++#define RTC_SW_CAL_MIN 0 ++#define RTC_TICKS_PER_SEC 32768 /* Number of ticks per second */ ++#define RTC_PPB_MULT 1000000000LL /* Multiplier for ppb conversions */ ++ ++/* RTC_HW_CAL_CFG */ ++#define RTC_HW_CAL_REF_SEL_SHIFT 0 ++#define RTC_HW_CAL_FRQ_SEL_SHIFT 1 ++ ++/* IRQ_EN/IRQ_EVEVT/IRQ_STATUS */ ++#define RTC_IRQ_CAL_START BIT(0) ++#define RTC_IRQ_CAL_FINISH BIT(1) ++#define RTC_IRQ_CMP BIT(2) ++#define RTC_IRQ_1SEC BIT(3) ++#define RTC_IRQ_ALAEM BIT(4) ++#define RTC_IRQ_EVT_UPDATE_PSE BIT(31) /* WO: Enable of update time&&date, IRQ_EVEVT only */ ++#define RTC_IRQ_ALL (RTC_IRQ_CAL_START \ ++ | RTC_IRQ_CAL_FINISH \ ++ | RTC_IRQ_CMP \ ++ | RTC_IRQ_1SEC \ ++ | RTC_IRQ_ALAEM) ++ ++/* CAL_VALUE */ ++#define RTC_CAL_VALUE_MASK GENMASK(15, 0) ++ ++/* CFG_TIME/ACT_TIME/RTC_TIME */ ++#define TIME_SEC_MASK GENMASK(6, 0) ++#define TIME_MIN_MASK GENMASK(13, 7) ++#define TIME_HOUR_MASK GENMASK(20, 14) ++ ++/* CFG_DATE/ACT_DATE/RTC_DATE */ ++#define DATE_DAY_MASK GENMASK(5, 0) ++#define DATE_MON_MASK GENMASK(10, 6) ++#define DATE_YEAR_MASK GENMASK(18, 11) ++ ++#define INT_TIMEOUT_US 180 ++ ++enum RTC_HOUR_MODE { ++ RTC_HOUR_MODE_12H = 0, ++ RTC_HOUR_MODE_24H = 1 ++}; ++ ++enum RTC_CAL_MODE { ++ RTC_CAL_MODE_SW = 0, ++ RTC_CAL_MODE_HW = 1 ++}; ++ ++enum RTC_HW_CAL_REF_MODE { ++ RTC_CAL_CLK_REF = 0, ++ RTC_CAL_CLK_MARK = 1 ++}; ++ ++static const unsigned long refclk_list[] = { ++ 1000000, ++ 2000000, ++ 4000000, ++ 5927000, ++ 6000000, ++ 7200000, ++ 8000000, ++ 10250000, ++ 11059200, ++ 12000000, ++ 12288000, ++ 13560000, ++ 16000000, ++ 19200000, ++ 20000000, ++ 22118000, ++ 24000000, ++ 24567000, ++ 25000000, ++ 26000000, ++ 27000000, ++ 30000000, ++ 32000000, ++ 33868800, ++ 36000000, ++ 36860000, ++ 40000000, ++ 44000000, ++ 50000000, ++ 54000000, ++ 28224000, ++ 28000000, ++}; ++ ++struct sft_rtc { ++ struct rtc_device *rtc_dev; ++ struct completion cal_done; ++ struct completion onesec_done; ++ struct clk *pclk; ++ struct clk *cal_clk; ++ struct reset_control *rst_array; ++ int hw_cal_map; ++ void __iomem *regs; ++ int rtc_irq; ++ int ms_pulse_irq; ++ int one_sec_pulse_irq; ++}; ++ ++static inline void sft_rtc_set_enabled(struct sft_rtc *srtc, bool enabled) ++{ ++ u32 val; ++ ++ if (enabled) { ++ val = readl(srtc->regs + SFT_RTC_CFG); ++ val |= BIT(RTC_CFG_ENABLE_SHIFT); ++ writel(val, srtc->regs + SFT_RTC_CFG); ++ } else { ++ val = readl(srtc->regs + SFT_RTC_CFG); ++ val &= ~BIT(RTC_CFG_ENABLE_SHIFT); ++ writel(val, srtc->regs + SFT_RTC_CFG); ++ } ++} ++ ++static inline bool sft_rtc_get_enabled(struct sft_rtc *srtc) ++{ ++ return !!(readl(srtc->regs + SFT_RTC_CFG) & BIT(RTC_CFG_ENABLE_SHIFT)); ++} ++ ++static inline void sft_rtc_set_mode(struct sft_rtc *srtc, enum RTC_HOUR_MODE mode) ++{ ++ u32 val; ++ ++ val = readl(srtc->regs + SFT_RTC_CFG); ++ val |= mode << RTC_CFG_HOUR_MODE_SHIFT; ++ writel(val, srtc->regs + SFT_RTC_CFG); ++} ++ ++static inline int sft_rtc_irq_enable(struct sft_rtc *srtc, u32 irq, bool enable) ++{ ++ u32 val; ++ ++ if (!(irq & RTC_IRQ_ALL)) ++ return -EINVAL; ++ ++ if (enable) { ++ val = readl(srtc->regs + SFT_RTC_IRQ_EN); ++ val |= irq; ++ writel(val, srtc->regs + SFT_RTC_IRQ_EN); ++ } else { ++ val = readl(srtc->regs + SFT_RTC_IRQ_EN); ++ val &= ~irq; ++ writel(val, srtc->regs + SFT_RTC_IRQ_EN); ++ } ++ return 0; ++} ++ ++static inline void ++sft_rtc_set_cal_hw_enable(struct sft_rtc *srtc, bool enable) ++{ ++ u32 val; ++ ++ if (enable) { ++ val = readl(srtc->regs + SFT_RTC_CFG); ++ val |= BIT(RTC_CFG_CAL_EN_HW_SHIFT); ++ writel(val, srtc->regs + SFT_RTC_CFG); ++ } else { ++ val = readl(srtc->regs + SFT_RTC_CFG); ++ val &= ~BIT(RTC_CFG_CAL_EN_HW_SHIFT); ++ writel(val, srtc->regs + SFT_RTC_CFG); ++ } ++} ++ ++static inline void ++sft_rtc_set_cal_mode(struct sft_rtc *srtc, enum RTC_CAL_MODE mode) ++{ ++ u32 val; ++ ++ val = readl(srtc->regs + SFT_RTC_CFG); ++ val |= mode << RTC_CFG_CAL_SEL_SHIFT; ++ writel(val, srtc->regs + SFT_RTC_CFG); ++} ++ ++static int sft_rtc_get_hw_calclk(struct device *dev, unsigned long freq) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(refclk_list); i++) ++ if (refclk_list[i] == freq) ++ return i; ++ ++ dev_err(dev, "refclk: %ldHz do not support.\n", freq); ++ return -EINVAL; ++} ++ ++static inline void sft_rtc_reg2time(struct rtc_time *tm, u32 reg) ++{ ++ tm->tm_hour = bcd2bin(FIELD_GET(TIME_HOUR_MASK, reg)); ++ tm->tm_min = bcd2bin(FIELD_GET(TIME_MIN_MASK, reg)); ++ tm->tm_sec = bcd2bin(FIELD_GET(TIME_SEC_MASK, reg)); ++} ++ ++static inline void sft_rtc_reg2date(struct rtc_time *tm, u32 reg) ++{ ++ tm->tm_year = bcd2bin(FIELD_GET(DATE_YEAR_MASK, reg)) + 100; ++ tm->tm_mon = bcd2bin(FIELD_GET(DATE_MON_MASK, reg)) - 1; ++ tm->tm_mday = bcd2bin(FIELD_GET(DATE_DAY_MASK, reg)); ++} ++ ++static inline u32 sft_rtc_time2reg(struct rtc_time *tm) ++{ ++ return FIELD_PREP(TIME_HOUR_MASK, bin2bcd(tm->tm_hour)) | ++ FIELD_PREP(TIME_MIN_MASK, bin2bcd(tm->tm_min)) | ++ FIELD_PREP(TIME_SEC_MASK, bin2bcd(tm->tm_sec)); ++} ++ ++static inline u32 sft_rtc_date2reg(struct rtc_time *tm) ++{ ++ return FIELD_PREP(DATE_YEAR_MASK, bin2bcd(tm->tm_year - 100)) | ++ FIELD_PREP(DATE_MON_MASK, bin2bcd(tm->tm_mon + 1)) | ++ FIELD_PREP(DATE_DAY_MASK, bin2bcd(tm->tm_mday)); ++} ++ ++static inline void sft_rtc_update_pulse(struct sft_rtc *srtc) ++{ ++ u32 val; ++ ++ val = readl(srtc->regs + SFT_RTC_IRQ_EVEVT); ++ val |= RTC_IRQ_EVT_UPDATE_PSE; ++ writel(val, srtc->regs + SFT_RTC_IRQ_EVEVT); ++} ++ ++static irqreturn_t sft_rtc_irq_handler(int irq, void *data) ++{ ++ struct sft_rtc *srtc = data; ++ struct timerqueue_node *next; ++ u32 irq_flags = 0; ++ u32 irq_mask = 0; ++ u32 val; ++ int ret = 0; ++ ++ val = readl(srtc->regs + SFT_RTC_IRQ_EVEVT); ++ if (val & RTC_IRQ_CAL_START) ++ irq_mask |= RTC_IRQ_CAL_START; ++ ++ if (val & RTC_IRQ_CAL_FINISH) { ++ irq_mask |= RTC_IRQ_CAL_FINISH; ++ complete(&srtc->cal_done); ++ } ++ ++ if (val & RTC_IRQ_CMP) ++ irq_mask |= RTC_IRQ_CMP; ++ ++ if (val & RTC_IRQ_1SEC) { ++ irq_flags |= RTC_PF; ++ irq_mask |= RTC_IRQ_1SEC; ++ complete(&srtc->onesec_done); ++ } ++ ++ if (val & RTC_IRQ_ALAEM) { ++ irq_flags |= RTC_AF; ++ irq_mask |= RTC_IRQ_ALAEM; ++ ++ next = timerqueue_getnext(&srtc->rtc_dev->timerqueue); ++ if (next == &srtc->rtc_dev->aie_timer.node) ++ dev_info(&srtc->rtc_dev->dev, "alarm expires"); ++ } ++ ++ writel(irq_mask, srtc->regs + SFT_RTC_IRQ_EVEVT); ++ ++ /* Wait interrupt flag clear */ ++ ret = readl_poll_timeout_atomic(srtc->regs + SFT_RTC_IRQ_EVEVT, val, ++ (val & irq_mask) == 0, 0, INT_TIMEOUT_US); ++ if (ret) ++ dev_warn(&srtc->rtc_dev->dev, "fail to clear rtc interrupt flag\n"); ++ ++ if (irq_flags) ++ rtc_update_irq(srtc->rtc_dev, 1, irq_flags | RTC_IRQF); ++ ++ return IRQ_HANDLED; ++} ++ ++static int sft_rtc_read_time(struct device *dev, struct rtc_time *tm) ++{ ++ struct sft_rtc *srtc = dev_get_drvdata(dev); ++ u32 val; ++ int irq_1sec_state_start, irq_1sec_state_end; ++ ++ /* If the RTC is disabled, assume the values are invalid */ ++ if (!sft_rtc_get_enabled(srtc)) ++ return -EINVAL; ++ ++ irq_1sec_state_start = ++ (readl(srtc->regs + SFT_RTC_IRQ_STATUS) & RTC_IRQ_1SEC) == 0 ? 0 : 1; ++ ++read_again: ++ val = readl(srtc->regs + SFT_RTC_TIME); ++ sft_rtc_reg2time(tm, val); ++ ++ val = readl(srtc->regs + SFT_RTC_DATE); ++ sft_rtc_reg2date(tm, val); ++ ++ if (irq_1sec_state_start == 0) { ++ irq_1sec_state_end = ++ (readl(srtc->regs + SFT_RTC_IRQ_STATUS) & RTC_IRQ_1SEC) == 0 ? 0 : 1; ++ if (irq_1sec_state_end == 1) { ++ irq_1sec_state_start = 1; ++ goto read_again; ++ } ++ } ++ ++ return 0; ++} ++ ++static int sft_rtc_set_time(struct device *dev, struct rtc_time *tm) ++{ ++ struct sft_rtc *srtc = dev_get_drvdata(dev); ++ u32 val; ++ int ret; ++ ++ val = sft_rtc_time2reg(tm); ++ writel(val, srtc->regs + SFT_RTC_CFG_TIME); ++ ++ val = sft_rtc_date2reg(tm); ++ writel(val, srtc->regs + SFT_RTC_CFG_DATE); ++ ++ /* Update pulse */ ++ sft_rtc_update_pulse(srtc); ++ ++ /* Ensure that data is fully written */ ++ ret = wait_for_completion_interruptible_timeout(&srtc->onesec_done, ++ usecs_to_jiffies(120)); ++ if (ret) { ++ dev_warn(dev, ++ "rtc wait for completion interruptible timeout.\n"); ++ } ++ return 0; ++} ++ ++static int sft_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) ++{ ++ struct sft_rtc *srtc = dev_get_drvdata(dev); ++ ++ return sft_rtc_irq_enable(srtc, RTC_IRQ_ALAEM, enabled); ++} ++ ++static int sft_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) ++{ ++ struct sft_rtc *srtc = dev_get_drvdata(dev); ++ u32 val; ++ ++ val = readl(srtc->regs + SFT_RTC_ACT_TIME); ++ sft_rtc_reg2time(&alarm->time, val); ++ ++ val = readl(srtc->regs + SFT_RTC_ACT_DATE); ++ sft_rtc_reg2date(&alarm->time, val); ++ ++ return 0; ++} ++ ++static int sft_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) ++{ ++ struct sft_rtc *srtc = dev_get_drvdata(dev); ++ u32 val; ++ ++ sft_rtc_alarm_irq_enable(dev, 0); ++ ++ val = sft_rtc_time2reg(&alarm->time); ++ writel(val, srtc->regs + SFT_RTC_ACT_TIME); ++ ++ val = sft_rtc_date2reg(&alarm->time); ++ writel(val, srtc->regs + SFT_RTC_ACT_DATE); ++ ++ sft_rtc_alarm_irq_enable(dev, alarm->enabled); ++ ++ return 0; ++} ++ ++static int sft_rtc_get_offset(struct device *dev, long *offset) ++{ ++ struct sft_rtc *srtc = dev_get_drvdata(dev); ++ s64 tmp; ++ u32 val; ++ ++ val = readl(srtc->regs + SFT_RTC_CAL_VALUE) ++ & RTC_SW_CAL_VALUE_MASK; ++ val += 1; ++ /* ++ * the adjust val range is [0x0000-0xffff], ++ * the default val is 0x7fff (32768-1),mapping offset=0 ; ++ */ ++ tmp = (s64)val - RTC_TICKS_PER_SEC; ++ tmp *= RTC_PPB_MULT; ++ tmp = div_s64(tmp, RTC_TICKS_PER_SEC); ++ ++ /* Offset value operates in negative way, so swap sign */ ++ *offset = -tmp; ++ ++ return 0; ++} ++ ++static int sft_rtc_set_offset(struct device *dev, long offset) ++{ ++ struct sft_rtc *srtc = dev_get_drvdata(dev); ++ s64 tmp; ++ u32 val; ++ ++ tmp = offset * RTC_TICKS_PER_SEC; ++ tmp = div_s64(tmp, RTC_PPB_MULT); ++ ++ tmp = RTC_TICKS_PER_SEC - tmp; ++ tmp -= 1; ++ if (tmp > RTC_SW_CAL_MAX || tmp < RTC_SW_CAL_MIN) { ++ dev_err(dev, "offset is out of range.\n"); ++ return -EINVAL; ++ } ++ ++ val = tmp & RTC_SW_CAL_VALUE_MASK; ++ /* set software calibration value */ ++ writel(val, srtc->regs + SFT_RTC_SW_CAL_VALUE); ++ ++ /* set CFG_RTC-cal_sel to select calibretion by software. */ ++ sft_rtc_set_cal_mode(srtc, RTC_CAL_MODE_SW); ++ ++ return 0; ++} ++ ++static __maybe_unused int ++sft_rtc_hw_adjustment(struct device *dev, unsigned int enable) ++{ ++ struct sft_rtc *srtc = dev_get_drvdata(dev); ++ u32 val; ++ ++ if (srtc->hw_cal_map <= 0) { ++ dev_err(dev, "fail to get cal-clock-freq.\n"); ++ return -EFAULT; ++ } ++ ++ if (enable) { ++ sft_rtc_irq_enable(srtc, RTC_IRQ_CAL_FINISH, true); ++ ++ /* Set reference clock frequency value */ ++ val = readl(srtc->regs + SFT_RTC_HW_CAL_CFG); ++ val |= (srtc->hw_cal_map << RTC_HW_CAL_FRQ_SEL_SHIFT); ++ writel(val, srtc->regs + SFT_RTC_HW_CAL_CFG); ++ ++ /* Set CFG_RTC-cal_sel to select calibretion by hardware. */ ++ sft_rtc_set_cal_mode(srtc, RTC_CAL_MODE_HW); ++ ++ /* Set CFG_RTC-cal_en_hw to launch hardware calibretion.*/ ++ sft_rtc_set_cal_hw_enable(srtc, true); ++ ++ wait_for_completion_interruptible_timeout(&srtc->cal_done, ++ usecs_to_jiffies(100)); ++ ++ sft_rtc_irq_enable(srtc, RTC_IRQ_CAL_FINISH, false); ++ } else { ++ sft_rtc_set_cal_mode(srtc, RTC_CAL_MODE_SW); ++ sft_rtc_set_cal_hw_enable(srtc, false); ++ } ++ ++ return 0; ++} ++ ++static int sft_rtc_get_cal_clk(struct device *dev, struct sft_rtc *srtc) ++{ ++ struct device_node *np = dev->of_node; ++ unsigned long cal_clk_freq; ++ u32 freq; ++ int ret; ++ ++ srtc->cal_clk = devm_clk_get(dev, "cal_clk"); ++ if (IS_ERR(srtc->cal_clk)) ++ return PTR_ERR(srtc->cal_clk); ++ ++ clk_prepare_enable(srtc->cal_clk); ++ ++ cal_clk_freq = clk_get_rate(srtc->cal_clk); ++ if (!cal_clk_freq) { ++ dev_warn(dev, ++ "get rate failed, next try to get from dts.\n"); ++ ret = of_property_read_u32(np, "rtc,cal-clock-freq", &freq); ++ if (!ret) { ++ cal_clk_freq = (u64)freq; ++ } else { ++ dev_err(dev, ++ "Need rtc,cal-clock-freq define in dts.\n"); ++ goto err_disable_cal_clk; ++ } ++ } ++ ++ srtc->hw_cal_map = sft_rtc_get_hw_calclk(dev, cal_clk_freq); ++ if (srtc->hw_cal_map < 0) { ++ ret = srtc->hw_cal_map; ++ goto err_disable_cal_clk; ++ } ++ ++ return 0; ++ ++err_disable_cal_clk: ++ clk_disable_unprepare(srtc->cal_clk); ++ ++ return ret; ++} ++ ++static int sft_rtc_get_irq(struct platform_device *pdev, struct sft_rtc *srtc) ++{ ++ int ret; ++ ++ srtc->rtc_irq = platform_get_irq_byname(pdev, "rtc"); ++ if (srtc->rtc_irq < 0) ++ return -EINVAL; ++ ++ ret = devm_request_irq(&pdev->dev, srtc->rtc_irq, ++ sft_rtc_irq_handler, 0, ++ KBUILD_MODNAME, srtc); ++ if (ret) ++ dev_err(&pdev->dev, "Failed to request interrupt, %d\n", ret); ++ ++ return ret; ++} ++ ++static const struct rtc_class_ops starfive_rtc_ops = { ++ .read_time = sft_rtc_read_time, ++ .set_time = sft_rtc_set_time, ++ .read_alarm = sft_rtc_read_alarm, ++ .set_alarm = sft_rtc_set_alarm, ++ .alarm_irq_enable = sft_rtc_alarm_irq_enable, ++ .set_offset = sft_rtc_set_offset, ++ .read_offset = sft_rtc_get_offset, ++}; ++ ++static int sft_rtc_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct sft_rtc *srtc; ++ struct rtc_time tm; ++ struct irq_desc *desc; ++ int ret; ++ ++ srtc = devm_kzalloc(dev, sizeof(*srtc), GFP_KERNEL); ++ if (!srtc) ++ return -ENOMEM; ++ ++ srtc->regs = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(srtc->regs)) ++ return PTR_ERR(srtc->regs); ++ ++ srtc->pclk = devm_clk_get(dev, "pclk"); ++ if (IS_ERR(srtc->pclk)) { ++ ret = PTR_ERR(srtc->pclk); ++ dev_err(dev, ++ "Failed to retrieve the peripheral clock, %d\n", ret); ++ return ret; ++ } ++ ++ srtc->rst_array = devm_reset_control_array_get_exclusive(dev); ++ if (IS_ERR(srtc->rst_array)) { ++ ret = PTR_ERR(srtc->rst_array); ++ dev_err(dev, ++ "Failed to retrieve the rtc reset, %d\n", ret); ++ return ret; ++ } ++ ++ init_completion(&srtc->cal_done); ++ init_completion(&srtc->onesec_done); ++ ++ ret = clk_prepare_enable(srtc->pclk); ++ if (ret) { ++ dev_err(dev, ++ "Failed to enable the peripheral clock, %d\n", ret); ++ return ret; ++ } ++ ++ ret = sft_rtc_get_cal_clk(dev, srtc); ++ if (ret) ++ goto err_disable_pclk; ++ ++ ret = reset_control_deassert(srtc->rst_array); ++ if (ret) { ++ dev_err(dev, ++ "Failed to deassert rtc resets, %d\n", ret); ++ goto err_disable_cal_clk; ++ } ++ ++ ret = sft_rtc_get_irq(pdev, srtc); ++ if (ret) ++ goto err_disable_cal_clk; ++ ++ srtc->rtc_dev = devm_rtc_allocate_device(dev); ++ if (IS_ERR(srtc->rtc_dev)) ++ return PTR_ERR(srtc->rtc_dev); ++ ++ platform_set_drvdata(pdev, srtc); ++ ++ /* The RTC supports 01.01.2001 - 31.12.2099 */ ++ srtc->rtc_dev->range_min = mktime64(2001, 1, 1, 0, 0, 0); ++ srtc->rtc_dev->range_max = mktime64(2099, 12, 31, 23, 59, 59); ++ ++ srtc->rtc_dev->ops = &starfive_rtc_ops; ++ device_init_wakeup(dev, true); ++ ++ desc = irq_to_desc(srtc->rtc_irq); ++ irq_desc_get_chip(desc)->flags = IRQCHIP_SKIP_SET_WAKE; ++ ++ /* Always use 24-hour mode and keep the RTC values */ ++ sft_rtc_set_mode(srtc, RTC_HOUR_MODE_24H); ++ ++ sft_rtc_set_enabled(srtc, true); ++ ++ if (device_property_read_bool(dev, "rtc,hw-adjustment")) ++ sft_rtc_hw_adjustment(dev, true); ++ ++ /* ++ * If rtc time is out of supported range, reset it to the minimum time. ++ * notice that, actual year = 1900 + tm.tm_year ++ * actual month = 1 + tm.tm_mon ++ */ ++ sft_rtc_read_time(dev, &tm); ++ if (tm.tm_year < 101 || tm.tm_year > 199 || tm.tm_mon < 0 || tm.tm_mon > 11 || ++ tm.tm_mday < 1 || tm.tm_mday > 31 || tm.tm_hour < 0 || tm.tm_hour > 23 || ++ tm.tm_min < 0 || tm.tm_min > 59 || tm.tm_sec < 0 || tm.tm_sec > 59) { ++ rtc_time64_to_tm(srtc->rtc_dev->range_min, &tm); ++ sft_rtc_set_time(dev, &tm); ++ } ++ ++ ret = devm_rtc_register_device(srtc->rtc_dev); ++ if (ret) ++ goto err_disable_wakeup; ++ ++ return 0; ++ ++err_disable_wakeup: ++ device_init_wakeup(dev, false); ++ ++err_disable_cal_clk: ++ clk_disable_unprepare(srtc->cal_clk); ++ ++err_disable_pclk: ++ clk_disable_unprepare(srtc->pclk); ++ ++ return ret; ++} ++ ++static int sft_rtc_remove(struct platform_device *pdev) ++{ ++ struct sft_rtc *srtc = platform_get_drvdata(pdev); ++ ++ sft_rtc_alarm_irq_enable(&pdev->dev, 0); ++ device_init_wakeup(&pdev->dev, 0); ++ ++ clk_disable_unprepare(srtc->pclk); ++ clk_disable_unprepare(srtc->cal_clk); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM_SLEEP ++static int sft_rtc_suspend(struct device *dev) ++{ ++ struct sft_rtc *srtc = dev_get_drvdata(dev); ++ ++ if (device_may_wakeup(dev)) ++ enable_irq_wake(srtc->rtc_irq); ++ ++ return 0; ++} ++ ++static int sft_rtc_resume(struct device *dev) ++{ ++ struct sft_rtc *srtc = dev_get_drvdata(dev); ++ ++ if (device_may_wakeup(dev)) ++ disable_irq_wake(srtc->rtc_irq); ++ ++ return 0; ++} ++#endif ++ ++static SIMPLE_DEV_PM_OPS(sft_rtc_pm_ops, sft_rtc_suspend, sft_rtc_resume); ++ ++static const struct of_device_id sft_rtc_of_match[] = { ++ { .compatible = "starfive,jh7110-rtc" }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, sft_rtc_of_match); ++ ++static struct platform_driver starfive_rtc_driver = { ++ .driver = { ++ .name = "starfive-rtc", ++ .of_match_table = sft_rtc_of_match, ++ .pm = &sft_rtc_pm_ops, ++ }, ++ .probe = sft_rtc_probe, ++ .remove = sft_rtc_remove, ++}; ++module_platform_driver(starfive_rtc_driver); ++ ++MODULE_AUTHOR("Samin Guo "); ++MODULE_AUTHOR("Hal Feng "); ++MODULE_DESCRIPTION("StarFive RTC driver"); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:starfive-rtc"); diff --git a/target/linux/starfive/patches-6.6/0066-uart-8250-Add-dw-auto-flow-ctrl-support.patch b/target/linux/starfive/patches-6.6/0066-uart-8250-Add-dw-auto-flow-ctrl-support.patch new file mode 100644 index 0000000000..c88008a604 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0066-uart-8250-Add-dw-auto-flow-ctrl-support.patch @@ -0,0 +1,96 @@ +From 552114b8cbbd956ad8466261b5f11b059eba82ca Mon Sep 17 00:00:00 2001 +From: Minda Chen +Date: Sun, 25 Jun 2023 09:40:29 +0800 +Subject: [PATCH 066/116] uart: 8250: Add dw auto flow ctrl support + +Add designeware 8250 auto flow ctrl support. Enable +it by add auto-flow-control in dts. + +Signed-off-by: Minda Chen +--- + drivers/tty/serial/8250/8250_core.c | 2 ++ + drivers/tty/serial/8250/8250_dw.c | 3 +++ + drivers/tty/serial/8250/8250_port.c | 14 +++++++++++++- + include/linux/serial_8250.h | 1 + + include/uapi/linux/serial_core.h | 2 ++ + 5 files changed, 21 insertions(+), 1 deletion(-) + +--- a/drivers/tty/serial/8250/8250_core.c ++++ b/drivers/tty/serial/8250/8250_core.c +@@ -1129,6 +1129,8 @@ int serial8250_register_8250_port(const + uart->dl_read = up->dl_read; + if (up->dl_write) + uart->dl_write = up->dl_write; ++ if (up->probe) ++ uart->probe = up->probe; + + if (uart->port.type != PORT_8250_CIR) { + if (serial8250_isa_config != NULL) +--- a/drivers/tty/serial/8250/8250_dw.c ++++ b/drivers/tty/serial/8250/8250_dw.c +@@ -612,6 +612,9 @@ static int dw8250_probe(struct platform_ + data->msr_mask_off |= UART_MSR_TERI; + } + ++ if (device_property_read_bool(dev, "auto-flow-control")) ++ up->probe |= UART_PROBE_AFE; ++ + /* If there is separate baudclk, get the rate from it. */ + data->clk = devm_clk_get_optional(dev, "baudclk"); + if (data->clk == NULL) +--- a/drivers/tty/serial/8250/8250_port.c ++++ b/drivers/tty/serial/8250/8250_port.c +@@ -330,6 +330,14 @@ static const struct serial8250_config ua + .rxtrig_bytes = {1, 8, 16, 30}, + .flags = UART_CAP_FIFO | UART_CAP_AFE, + }, ++ [PORT_16550A_AFE] = { ++ .name = "16550A_AFE", ++ .fifo_size = 16, ++ .tx_loadsz = 16, ++ .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, ++ .rxtrig_bytes = {1, 4, 8, 14}, ++ .flags = UART_CAP_FIFO | UART_CAP_AFE, ++ }, + }; + + /* Uart divisor latch read */ +@@ -1143,6 +1151,11 @@ static void autoconfig_16550a(struct uar + up->port.type = PORT_U6_16550A; + up->capabilities |= UART_CAP_AFE; + } ++ ++ if ((up->port.type == PORT_16550A) && (up->probe & UART_PROBE_AFE)) { ++ up->port.type = PORT_16550A_AFE; ++ up->capabilities |= UART_CAP_AFE; ++ } + } + + /* +@@ -2813,7 +2826,6 @@ serial8250_do_set_termios(struct uart_po + if (termios->c_cflag & CRTSCTS) + up->mcr |= UART_MCR_AFE; + } +- + /* + * Update the per-port timeout. + */ +--- a/include/linux/serial_8250.h ++++ b/include/linux/serial_8250.h +@@ -141,6 +141,7 @@ struct uart_8250_port { + unsigned char probe; + struct mctrl_gpios *gpios; + #define UART_PROBE_RSA (1 << 0) ++#define UART_PROBE_AFE (1 << 1) + + /* + * Some bits in registers are cleared on a read, so they must +--- a/include/uapi/linux/serial_core.h ++++ b/include/uapi/linux/serial_core.h +@@ -245,4 +245,6 @@ + /* Sunplus UART */ + #define PORT_SUNPLUS 123 + ++#define PORT_16550A_AFE 124 ++ + #endif /* _UAPILINUX_SERIAL_CORE_H */ diff --git a/target/linux/starfive/patches-6.6/0067-driver-uart-fix-up-uart-communicate-fail.patch b/target/linux/starfive/patches-6.6/0067-driver-uart-fix-up-uart-communicate-fail.patch new file mode 100644 index 0000000000..18d735b92d --- /dev/null +++ b/target/linux/starfive/patches-6.6/0067-driver-uart-fix-up-uart-communicate-fail.patch @@ -0,0 +1,29 @@ +From 6edee93a89254f30c3387c88231e7ecec06ba84a Mon Sep 17 00:00:00 2001 +From: "shanlong.li" +Date: Mon, 10 Jul 2023 03:07:57 -0700 +Subject: [PATCH 067/116] driver:uart: fix up uart communicate fail + +fix up uart communicate fail + +Signed-off-by: shanlong.li +--- + drivers/tty/serial/8250/8250_dw.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +--- a/drivers/tty/serial/8250/8250_dw.c ++++ b/drivers/tty/serial/8250/8250_dw.c +@@ -652,10 +652,10 @@ static int dw8250_probe(struct platform_ + if (err) + return err; + +- data->rst = devm_reset_control_get_optional_exclusive(dev, NULL); +- if (IS_ERR(data->rst)) +- return PTR_ERR(data->rst); +- ++ data->rst = devm_reset_control_array_get_exclusive(dev); ++ if (IS_ERR(data->rst)) { ++ err = PTR_ERR(data->rst); ++ } + reset_control_deassert(data->rst); + + err = devm_add_action_or_reset(dev, dw8250_reset_control_assert, data->rst); diff --git a/target/linux/starfive/patches-6.6/0068-uart-8250-add-reset-operation-in-runtime-PM.patch b/target/linux/starfive/patches-6.6/0068-uart-8250-add-reset-operation-in-runtime-PM.patch new file mode 100644 index 0000000000..56efebda6c --- /dev/null +++ b/target/linux/starfive/patches-6.6/0068-uart-8250-add-reset-operation-in-runtime-PM.patch @@ -0,0 +1,32 @@ +From 777d288f03a0b350f6c2d4367b01a80d9f25cd6e Mon Sep 17 00:00:00 2001 +From: William Qiu +Date: Wed, 20 Sep 2023 17:19:59 +0800 +Subject: [PATCH 068/116] uart: 8250: add reset operation in runtime PM + +add reset operation in runtime PM + +Signed-off-by: William Qiu +--- + drivers/tty/serial/8250/8250_dw.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/drivers/tty/serial/8250/8250_dw.c ++++ b/drivers/tty/serial/8250/8250_dw.c +@@ -745,6 +745,8 @@ static int dw8250_runtime_suspend(struct + { + struct dw8250_data *data = dev_get_drvdata(dev); + ++ reset_control_assert(data->rst); ++ + clk_disable_unprepare(data->clk); + + clk_disable_unprepare(data->pclk); +@@ -760,6 +762,8 @@ static int dw8250_runtime_resume(struct + + clk_prepare_enable(data->clk); + ++ reset_control_deassert(data->rst); ++ + return 0; + } + diff --git a/target/linux/starfive/patches-6.6/0069-dt-bindings-CAN-Add-StarFive-CAN-module.patch b/target/linux/starfive/patches-6.6/0069-dt-bindings-CAN-Add-StarFive-CAN-module.patch new file mode 100644 index 0000000000..eb46a96314 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0069-dt-bindings-CAN-Add-StarFive-CAN-module.patch @@ -0,0 +1,113 @@ +From 5eda2331a252436756fb40861f01a7a38b1502c7 Mon Sep 17 00:00:00 2001 +From: William Qiu +Date: Thu, 15 Jun 2023 20:14:22 +0800 +Subject: [PATCH 069/116] dt-bindings: CAN: Add StarFive CAN module + +Add documentation to describe StarFive CAN engine. + +Signed-off-by: William Qiu +--- + .../devicetree/bindings/net/can/ipms-can.yaml | 97 +++++++++++++++++++ + 1 file changed, 97 insertions(+) + create mode 100644 Documentation/devicetree/bindings/net/can/ipms-can.yaml + +--- /dev/null ++++ b/Documentation/devicetree/bindings/net/can/ipms-can.yaml +@@ -0,0 +1,97 @@ ++# SPDX-License-Identifier: GPL-2.0 ++%YAML 1.2 ++--- ++#$id: http://devicetree.org/schemas/net/can/ipms-can.yaml# ++#$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: IPMS CAN/CANFD controller Device Tree Bindings ++ ++properties: ++ compatible: ++ const:ipms,can ++ ++ reg: ++ maxItems: 1 ++ items: ++ - description:CAN controller registers ++ ++ interrupts: ++ maxItems: 1 ++ ++ clocks: ++ minItems: 1 ++ items: ++ - description:apb_clk clock ++ - description:core_clk clock ++ - description:timer_clk clock ++ ++ clock-names: ++ minItems: 1 ++ items: ++ - const:apb_clk ++ - const:core_clk ++ - const:timer_clk ++ resets: ++ minItems: 1 ++ items: ++ - description:apb_clk reset ++ - description:core_clk reset ++ - description:timer_clk reset ++ reset-names: ++ minItems: 1 ++ items: ++ - const:rst_apb ++ - const:rst_core ++ - const:rst_timer ++ starfive,sys-syscon: ++ format: ++ starfive,sys-syscon = <&arg0 arg1 arg2 arg3> ++ description: ++ arg0:arg0 is sys_syscon. ++ arg1:arg1 is syscon register offset, used to enable can2.0/canfd function, can0 is 0x10, can1 is 0x88. ++ arg2:arg2 is used to enable the register shift of the can2.0/canfd function, can0 is 0x3, can1 is 0x12. ++ arg3:arg3 is used to enable the register mask of the can2.0/canfd function, can0 is 0x8, can1 is 0x40000 ++ ++ syscon,can_or_canfd: ++ description: ++ IPMS CAN-CTRL core is a serial communications controller that performs serial communication according to the CAN protocol. ++ This CAN bus interface uses the basic CAN principle and meets all constraints of the CAN-specification 2.0B active. ++ Furthermore this CAN core can be configured to meet the specification of CAN with flexible data rate CAN FD. ++ When syscon,can_or_canfd is set to 0, use CAN2.0B. ++ when syscon,can_or_canfd is set to 1, use CAN FD. ++required: ++ - compatible ++ - reg ++ - interrupts ++ - clocks ++ - clock-names ++ - resets ++ - reset-names ++ - starfive,sys-syscon ++ - syscon,can_or_canfd ++additionalProperties:false ++ ++examples: ++ - | ++ can0: can@130d0000{ ++ compatible = "ipms,can"; ++ reg = <0x0 0x130d0000 0x0 0x1000>; ++ interrupts = <112>; ++ interrupt-parent = <&plic>; ++ clocks = <&clkgen JH7110_CAN0_CTRL_CLK_APB>, ++ <&clkgen JH7110_CAN0_CTRL_CLK_CAN>, ++ <&clkgen JH7110_CAN0_CTRL_CLK_TIMER>; ++ clock-names = "apb_clk", ++ "core_clk", ++ "timer_clk"; ++ resets = <&rstgen RSTN_U0_CAN_CTRL_APB>, ++ <&rstgen RSTN_U0_CAN_CTRL_CORE>, ++ <&rstgen RSTN_U0_CAN_CTRL_TIMER>; ++ reset-names = "rst_apb", ++ "rst_core", ++ "rst_timer"; ++ starfive,sys-syscon = <&sys_syscon, 0x10 0x3 0x8>; ++ syscon,can_or_canfd = <0>; ++ }; ++ ++... diff --git a/target/linux/starfive/patches-6.6/0070-CAN-starfive-Add-CAN-engine-support.patch b/target/linux/starfive/patches-6.6/0070-CAN-starfive-Add-CAN-engine-support.patch new file mode 100644 index 0000000000..1bb41dd079 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0070-CAN-starfive-Add-CAN-engine-support.patch @@ -0,0 +1,1317 @@ +From b1fbe15b87be654b1b280a76ec1470917d79f720 Mon Sep 17 00:00:00 2001 +From: William Qiu +Date: Thu, 15 Jun 2023 20:15:25 +0800 +Subject: [PATCH 070/116] CAN: starfive - Add CAN engine support + +Adding device probe StarFive CAN module. + +Signed-off-by: William Qiu +Signed-off-by: Hal Feng +--- + drivers/net/can/Kconfig | 5 + + drivers/net/can/Makefile | 1 + + drivers/net/can/ipms_canfd.c | 1275 ++++++++++++++++++++++++++++++++++ + 3 files changed, 1281 insertions(+) + create mode 100644 drivers/net/can/ipms_canfd.c + +--- a/drivers/net/can/Kconfig ++++ b/drivers/net/can/Kconfig +@@ -214,6 +214,11 @@ config CAN_XILINXCAN + Xilinx CAN driver. This driver supports both soft AXI CAN IP and + Zynq CANPS IP. + ++config IPMS_CAN ++ tristate "IPMS CAN" ++ help ++ IPMS CANFD driver. This driver supports IPMS CANFD IP. ++ + source "drivers/net/can/c_can/Kconfig" + source "drivers/net/can/cc770/Kconfig" + source "drivers/net/can/ctucanfd/Kconfig" +--- a/drivers/net/can/Makefile ++++ b/drivers/net/can/Makefile +@@ -31,5 +31,6 @@ obj-$(CONFIG_CAN_SJA1000) += sja1000/ + obj-$(CONFIG_CAN_SUN4I) += sun4i_can.o + obj-$(CONFIG_CAN_TI_HECC) += ti_hecc.o + obj-$(CONFIG_CAN_XILINXCAN) += xilinx_can.o ++obj-$(CONFIG_IPMS_CAN) += ipms_canfd.o + + subdir-ccflags-$(CONFIG_CAN_DEBUG_DEVICES) += -DDEBUG +--- /dev/null ++++ b/drivers/net/can/ipms_canfd.c +@@ -0,0 +1,1275 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * StarFive Controller Area Network Host Controller Driver ++ * ++ * Copyright (c) 2022 StarFive Technology Co., Ltd. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define DRIVER_NAME "ipms_canfd" ++ ++/* CAN registers set */ ++enum canfd_device_reg { ++ CANFD_RUBF_OFFSET = 0x00, /* Receive Buffer Registers 0x00-0x4f */ ++ CANFD_RUBF_ID_OFFSET = 0x00, ++ CANFD_RBUF_CTL_OFFSET = 0x04, ++ CANFD_RBUF_DATA_OFFSET = 0x08, ++ CANFD_TBUF_OFFSET = 0x50, /* Transmit Buffer Registers 0x50-0x97 */ ++ CANFD_TBUF_ID_OFFSET = 0x50, ++ CANFD_TBUF_CTL_OFFSET = 0x54, ++ CANFD_TBUF_DATA_OFFSET = 0x58, ++ CANFD_TTS_OFFSET = 0x98, /* Transmission Time Stamp 0x98-0x9f */ ++ CANFD_CFG_STAT_OFFSET = 0xa0, ++ CANFD_TCMD_OFFSET = 0xa1, ++ CANFD_TCTRL_OFFSET = 0xa2, ++ CANFD_RCTRL_OFFSET = 0xa3, ++ CANFD_RTIE_OFFSET = 0xa4, ++ CANFD_RTIF_OFFSET = 0xa5, ++ CANFD_ERRINT_OFFSET = 0xa6, ++ CANFD_LIMIT_OFFSET = 0xa7, ++ CANFD_S_SEG_1_OFFSET = 0xa8, ++ CANFD_S_SEG_2_OFFSET = 0xa9, ++ CANFD_S_SJW_OFFSET = 0xaa, ++ CANFD_S_PRESC_OFFSET = 0xab, ++ CANFD_F_SEG_1_OFFSET = 0xac, ++ CANFD_F_SEG_2_OFFSET = 0xad, ++ CANFD_F_SJW_OFFSET = 0xae, ++ CANFD_F_PRESC_OFFSET = 0xaf, ++ CANFD_EALCAP_OFFSET = 0xb0, ++ CANFD_RECNT_OFFSET = 0xb2, ++ CANFD_TECNT_OFFSET = 0xb3, ++}; ++ ++enum canfd_reg_bitchange { ++ CAN_FD_SET_RST_MASK = 0x80, /* Set Reset Bit */ ++ CAN_FD_OFF_RST_MASK = 0x7f, /* Reset Off Bit */ ++ CAN_FD_SET_FULLCAN_MASK = 0x10, /* set TTTBM as 1->full TTCAN mode */ ++ CAN_FD_OFF_FULLCAN_MASK = 0xef, /* set TTTBM as 0->separate PTB and STB mode */ ++ CAN_FD_SET_FIFO_MASK = 0x20, /* set TSMODE as 1->FIFO mode */ ++ CAN_FD_OFF_FIFO_MASK = 0xdf, /* set TSMODE as 0->Priority mode */ ++ CAN_FD_SET_TSONE_MASK = 0x04, ++ CAN_FD_OFF_TSONE_MASK = 0xfb, ++ CAN_FD_SET_TSALL_MASK = 0x02, ++ CAN_FD_OFF_TSALL_MASK = 0xfd, ++ CAN_FD_LBMEMOD_MASK = 0x40, /* set loop back mode, external */ ++ CAN_FD_LBMIMOD_MASK = 0x20, /* set loopback internal mode */ ++ CAN_FD_SET_BUSOFF_MASK = 0x01, ++ CAN_FD_OFF_BUSOFF_MASK = 0xfe, ++ CAN_FD_SET_TTSEN_MASK = 0x80, /* set ttsen, tts update enable */ ++ CAN_FD_SET_BRS_MASK = 0x10, /* can fd Bit Rate Switch mask */ ++ CAN_FD_OFF_BRS_MASK = 0xef, ++ CAN_FD_SET_EDL_MASK = 0x20, /* Extended Data Length */ ++ CAN_FD_OFF_EDL_MASK = 0xdf, ++ CAN_FD_SET_DLC_MASK = 0x0f, ++ CAN_FD_SET_TENEXT_MASK = 0x40, ++ CAN_FD_SET_IDE_MASK = 0x80, ++ CAN_FD_OFF_IDE_MASK = 0x7f, ++ CAN_FD_SET_RTR_MASK = 0x40, ++ CAN_FD_OFF_RTR_MASK = 0xbf, ++ CAN_FD_INTR_ALL_MASK = 0xff, /* all interrupts enable mask */ ++ CAN_FD_SET_RIE_MASK = 0x80, ++ CAN_FD_OFF_RIE_MASK = 0x7f, ++ CAN_FD_SET_RFIE_MASK = 0x20, ++ CAN_FD_OFF_RFIE_MASK = 0xdf, ++ CAN_FD_SET_RAFIE_MASK = 0x10, ++ CAN_FD_OFF_RAFIE_MASK = 0xef, ++ CAN_FD_SET_EIE_MASK = 0x02, ++ CAN_FD_OFF_EIE_MASK = 0xfd, ++ CAN_FD_TASCTIVE_MASK = 0x02, ++ CAN_FD_RASCTIVE_MASK = 0x04, ++ CAN_FD_SET_TBSEL_MASK = 0x80, /* message writen in STB */ ++ CAN_FD_OFF_TBSEL_MASK = 0x7f, /* message writen in PTB */ ++ CAN_FD_SET_STBY_MASK = 0x20, ++ CAN_FD_OFF_STBY_MASK = 0xdf, ++ CAN_FD_SET_TPE_MASK = 0x10, /* Transmit primary enable */ ++ CAN_FD_SET_TPA_MASK = 0x08, ++ CAN_FD_SET_SACK_MASK = 0x80, ++ CAN_FD_SET_RREL_MASK = 0x10, ++ CAN_FD_RSTAT_NOT_EMPTY_MASK = 0x03, ++ CAN_FD_SET_RIF_MASK = 0x80, ++ CAN_FD_OFF_RIF_MASK = 0x7f, ++ CAN_FD_SET_RAFIF_MASK = 0x10, ++ CAN_FD_SET_RFIF_MASK = 0x20, ++ CAN_FD_SET_TPIF_MASK = 0x08, /* Transmission Primary Interrupt Flag */ ++ CAN_FD_SET_TSIF_MASK = 0x04, ++ CAN_FD_SET_EIF_MASK = 0x02, ++ CAN_FD_SET_AIF_MASK = 0x01, ++ CAN_FD_SET_EWARN_MASK = 0x80, ++ CAN_FD_SET_EPASS_MASK = 0x40, ++ CAN_FD_SET_EPIE_MASK = 0x20, ++ CAN_FD_SET_EPIF_MASK = 0x10, ++ CAN_FD_SET_ALIE_MASK = 0x08, ++ CAN_FD_SET_ALIF_MASK = 0x04, ++ CAN_FD_SET_BEIE_MASK = 0x02, ++ CAN_FD_SET_BEIF_MASK = 0x01, ++ CAN_FD_OFF_EPIE_MASK = 0xdf, ++ CAN_FD_OFF_BEIE_MASK = 0xfd, ++ CAN_FD_SET_AFWL_MASK = 0x40, ++ CAN_FD_SET_EWL_MASK = 0x0b, ++ CAN_FD_SET_KOER_MASK = 0xe0, ++ CAN_FD_SET_BIT_ERROR_MASK = 0x20, ++ CAN_FD_SET_FORM_ERROR_MASK = 0x40, ++ CAN_FD_SET_STUFF_ERROR_MASK = 0x60, ++ CAN_FD_SET_ACK_ERROR_MASK = 0x80, ++ CAN_FD_SET_CRC_ERROR_MASK = 0xa0, ++ CAN_FD_SET_OTH_ERROR_MASK = 0xc0, ++}; ++ ++/* seg1,seg2,sjw,prescaler all have 8 bits */ ++#define BITS_OF_BITTIMING_REG 8 ++ ++/* in can_bittiming strucure every field has 32 bits---->u32 */ ++#define FBITS_IN_BITTIMING_STR 32 ++#define SEG_1_SHIFT 0 ++#define SEG_2_SHIFT 8 ++#define SJW_SHIFT 16 ++#define PRESC_SHIFT 24 ++ ++/* TTSEN bit used for 32 bit register read or write */ ++#define TTSEN_8_32_SHIFT 24 ++#define RTR_32_8_SHIFT 24 ++ ++/* transmit mode */ ++#define XMIT_FULL 0 ++#define XMIT_SEP_FIFO 1 ++#define XMIT_SEP_PRIO 2 ++#define XMIT_PTB_MODE 3 ++ ++enum IPMS_CAN_TYPE { ++ IPMS_CAN_TYPY_CAN = 0, ++ IPMS_CAN_TYPE_CANFD, ++}; ++ ++struct ipms_canfd_priv { ++ struct can_priv can; ++ struct napi_struct napi; ++ struct device *dev; ++ struct regmap *reg_syscon; ++ void __iomem *reg_base; ++ u32 (*read_reg)(const struct ipms_canfd_priv *priv, enum canfd_device_reg reg); ++ void (*write_reg)(const struct ipms_canfd_priv *priv, enum canfd_device_reg reg, u32 val); ++ struct clk *can_clk; ++ u32 tx_mode; ++ struct reset_control *resets; ++ struct clk_bulk_data *clks; ++ int nr_clks; ++ u32 can_or_canfd; ++}; ++ ++static struct can_bittiming_const canfd_bittiming_const = { ++ .name = DRIVER_NAME, ++ .tseg1_min = 2, ++ .tseg1_max = 16, ++ .tseg2_min = 2, ++ .tseg2_max = 8, ++ .sjw_max = 4, ++ .brp_min = 1, ++ .brp_max = 512, ++ .brp_inc = 1, ++ ++}; ++ ++static struct can_bittiming_const canfd_data_bittiming_const = { ++ .name = DRIVER_NAME, ++ .tseg1_min = 1, ++ .tseg1_max = 16, ++ .tseg2_min = 2, ++ .tseg2_max = 8, ++ .sjw_max = 8, ++ .brp_min = 1, ++ .brp_max = 512, ++ .brp_inc = 1, ++}; ++ ++static void canfd_write_reg_le(const struct ipms_canfd_priv *priv, ++ enum canfd_device_reg reg, u32 val) ++{ ++ iowrite32(val, priv->reg_base + reg); ++} ++ ++static u32 canfd_read_reg_le(const struct ipms_canfd_priv *priv, ++ enum canfd_device_reg reg) ++{ ++ return ioread32(priv->reg_base + reg); ++} ++ ++static inline unsigned char can_ioread8(const void *addr) ++{ ++ void *addr_down; ++ union val { ++ u8 val_8[4]; ++ u32 val_32; ++ } val; ++ u32 offset = 0; ++ ++ addr_down = (void *)ALIGN_DOWN((unsigned long)addr, 4); ++ offset = addr - addr_down; ++ val.val_32 = ioread32(addr_down); ++ return val.val_8[offset]; ++} ++ ++static inline void can_iowrite8(unsigned char value, void *addr) ++{ ++ void *addr_down; ++ union val { ++ u8 val_8[4]; ++ u32 val_32; ++ } val; ++ u8 offset = 0; ++ ++ addr_down = (void *)ALIGN_DOWN((unsigned long)addr, 4); ++ offset = addr - addr_down; ++ val.val_32 = ioread32(addr_down); ++ val.val_8[offset] = value; ++ iowrite32(val.val_32, addr_down); ++} ++ ++static void canfd_reigister_set_bit(const struct ipms_canfd_priv *priv, ++ enum canfd_device_reg reg, ++ enum canfd_reg_bitchange set_mask) ++{ ++ void *addr_down; ++ union val { ++ u8 val_8[4]; ++ u32 val_32; ++ } val; ++ u8 offset = 0; ++ ++ addr_down = (void *)ALIGN_DOWN((unsigned long)(priv->reg_base + reg), 4); ++ offset = (priv->reg_base + reg) - addr_down; ++ val.val_32 = ioread32(addr_down); ++ val.val_8[offset] |= set_mask; ++ iowrite32(val.val_32, addr_down); ++} ++ ++static void canfd_reigister_off_bit(const struct ipms_canfd_priv *priv, ++ enum canfd_device_reg reg, ++ enum canfd_reg_bitchange set_mask) ++{ ++ void *addr_down; ++ union val { ++ u8 val_8[4]; ++ u32 val_32; ++ } val; ++ u8 offset = 0; ++ ++ addr_down = (void *)ALIGN_DOWN((unsigned long)(priv->reg_base + reg), 4); ++ offset = (priv->reg_base + reg) - addr_down; ++ val.val_32 = ioread32(addr_down); ++ val.val_8[offset] &= set_mask; ++ iowrite32(val.val_32, addr_down); ++} ++ ++static int canfd_device_driver_bittime_configuration(struct net_device *ndev) ++{ ++ struct ipms_canfd_priv *priv = netdev_priv(ndev); ++ struct can_bittiming *bt = &priv->can.bittiming; ++ struct can_bittiming *dbt = &priv->can.data_bittiming; ++ u32 reset_test, bittiming_temp, dat_bittiming; ++ ++ reset_test = can_ioread8(priv->reg_base + CANFD_CFG_STAT_OFFSET); ++ ++ if (!(reset_test & CAN_FD_SET_RST_MASK)) { ++ netdev_alert(ndev, "Not in reset mode, cannot set bit timing\n"); ++ return -EPERM; ++ } ++ ++ bittiming_temp = ((bt->phase_seg1 + bt->prop_seg + 1 - 2) << SEG_1_SHIFT) | ++ ((bt->phase_seg2 - 1) << SEG_2_SHIFT) | ++ ((bt->sjw - 1) << SJW_SHIFT) | ++ ((bt->brp - 1) << PRESC_SHIFT); ++ ++ /* Check the bittime parameter */ ++ if ((((int)(bt->phase_seg1 + bt->prop_seg + 1) - 2) < 0) || ++ (((int)(bt->phase_seg2) - 1) < 0) || ++ (((int)(bt->sjw) - 1) < 0) || ++ (((int)(bt->brp) - 1) < 0)) ++ return -EINVAL; ++ ++ priv->write_reg(priv, CANFD_S_SEG_1_OFFSET, bittiming_temp); ++ ++ if (priv->can_or_canfd == IPMS_CAN_TYPE_CANFD) { ++ dat_bittiming = ((dbt->phase_seg1 + dbt->prop_seg + 1 - 2) << SEG_1_SHIFT) | ++ ((dbt->phase_seg2 - 1) << SEG_2_SHIFT) | ++ ((dbt->sjw - 1) << SJW_SHIFT) | ++ ((dbt->brp - 1) << PRESC_SHIFT); ++ ++ if ((((int)(dbt->phase_seg1 + dbt->prop_seg + 1) - 2) < 0) || ++ (((int)(dbt->phase_seg2) - 1) < 0) || ++ (((int)(dbt->sjw) - 1) < 0) || ++ (((int)(dbt->brp) - 1) < 0)) ++ return -EINVAL; ++ ++ priv->write_reg(priv, CANFD_F_SEG_1_OFFSET, dat_bittiming); ++ } ++ ++ canfd_reigister_off_bit(priv, CANFD_CFG_STAT_OFFSET, CAN_FD_OFF_RST_MASK); ++ ++ netdev_dbg(ndev, "Slow bit rate: %08x\n", priv->read_reg(priv, CANFD_S_SEG_1_OFFSET)); ++ netdev_dbg(ndev, "Fast bit rate: %08x\n", priv->read_reg(priv, CANFD_F_SEG_1_OFFSET)); ++ ++ return 0; ++} ++ ++int canfd_get_freebuffer(struct ipms_canfd_priv *priv) ++{ ++ /* Get next transmit buffer */ ++ canfd_reigister_set_bit(priv, CANFD_TCTRL_OFFSET, CAN_FD_SET_TENEXT_MASK); ++ ++ if (can_ioread8(priv->reg_base + CANFD_TCTRL_OFFSET) & CAN_FD_SET_TENEXT_MASK) ++ return -1; ++ ++ return 0; ++} ++ ++static void canfd_tx_interrupt(struct net_device *ndev, u8 isr) ++{ ++ struct ipms_canfd_priv *priv = netdev_priv(ndev); ++ ++ /* wait till transmission of the PTB or STB finished */ ++ while (isr & (CAN_FD_SET_TPIF_MASK | CAN_FD_SET_TSIF_MASK)) { ++ if (isr & CAN_FD_SET_TPIF_MASK) ++ canfd_reigister_set_bit(priv, CANFD_RTIF_OFFSET, CAN_FD_SET_TPIF_MASK); ++ ++ if (isr & CAN_FD_SET_TSIF_MASK) ++ canfd_reigister_set_bit(priv, CANFD_RTIF_OFFSET, CAN_FD_SET_TSIF_MASK); ++ ++ isr = can_ioread8(priv->reg_base + CANFD_RTIF_OFFSET); ++ } ++ netif_wake_queue(ndev); ++} ++ ++static int can_rx(struct net_device *ndev) ++{ ++ struct ipms_canfd_priv *priv = netdev_priv(ndev); ++ struct net_device_stats *stats = &ndev->stats; ++ struct can_frame *cf; ++ struct sk_buff *skb; ++ u32 can_id; ++ u8 dlc, control, rx_status; ++ ++ rx_status = can_ioread8(priv->reg_base + CANFD_RCTRL_OFFSET); ++ ++ if (!(rx_status & CAN_FD_RSTAT_NOT_EMPTY_MASK)) ++ return 0; ++ control = can_ioread8(priv->reg_base + CANFD_RBUF_CTL_OFFSET); ++ can_id = priv->read_reg(priv, CANFD_RUBF_ID_OFFSET); ++ dlc = can_ioread8(priv->reg_base + CANFD_RBUF_CTL_OFFSET) & CAN_FD_SET_DLC_MASK; ++ ++ skb = alloc_can_skb(ndev, (struct can_frame **)&cf); ++ if (!skb) { ++ stats->rx_dropped++; ++ return 0; ++ } ++ cf->can_dlc = can_cc_dlc2len(dlc); ++ ++ /* change the CANFD id into socketcan id format */ ++ if (control & CAN_FD_SET_IDE_MASK) { ++ cf->can_id = can_id; ++ cf->can_id |= CAN_EFF_FLAG; ++ } else { ++ cf->can_id = can_id; ++ cf->can_id &= (~CAN_EFF_FLAG); ++ } ++ ++ if (control & CAN_FD_SET_RTR_MASK) ++ cf->can_id |= CAN_RTR_FLAG; ++ ++ if (!(control & CAN_FD_SET_RTR_MASK)) { ++ *((u32 *)(cf->data + 0)) = priv->read_reg(priv, CANFD_RBUF_DATA_OFFSET); ++ *((u32 *)(cf->data + 4)) = priv->read_reg(priv, CANFD_RBUF_DATA_OFFSET + 4); ++ } ++ ++ canfd_reigister_set_bit(priv, CANFD_RCTRL_OFFSET, CAN_FD_SET_RREL_MASK); ++ stats->rx_bytes += can_fd_dlc2len(cf->can_dlc); ++ stats->rx_packets++; ++ netif_receive_skb(skb); ++ ++ return 1; ++} ++ ++static int canfd_rx(struct net_device *ndev) ++{ ++ struct ipms_canfd_priv *priv = netdev_priv(ndev); ++ struct net_device_stats *stats = &ndev->stats; ++ struct canfd_frame *cf; ++ struct sk_buff *skb; ++ u32 can_id; ++ u8 dlc, control, rx_status; ++ int i; ++ ++ rx_status = can_ioread8(priv->reg_base + CANFD_RCTRL_OFFSET); ++ ++ if (!(rx_status & CAN_FD_RSTAT_NOT_EMPTY_MASK)) ++ return 0; ++ control = can_ioread8(priv->reg_base + CANFD_RBUF_CTL_OFFSET); ++ can_id = priv->read_reg(priv, CANFD_RUBF_ID_OFFSET); ++ dlc = can_ioread8(priv->reg_base + CANFD_RBUF_CTL_OFFSET) & CAN_FD_SET_DLC_MASK; ++ ++ if (control & CAN_FD_SET_EDL_MASK) ++ /* allocate sk_buffer for canfd frame */ ++ skb = alloc_canfd_skb(ndev, &cf); ++ else ++ /* allocate sk_buffer for can frame */ ++ skb = alloc_can_skb(ndev, (struct can_frame **)&cf); ++ ++ if (!skb) { ++ stats->rx_dropped++; ++ return 0; ++ } ++ ++ /* change the CANFD or CAN2.0 data into socketcan data format */ ++ if (control & CAN_FD_SET_EDL_MASK) ++ cf->len = can_fd_dlc2len(dlc); ++ else ++ cf->len = can_cc_dlc2len(dlc); ++ ++ /* change the CANFD id into socketcan id format */ ++ if (control & CAN_FD_SET_EDL_MASK) { ++ cf->can_id = can_id; ++ if (control & CAN_FD_SET_IDE_MASK) ++ cf->can_id |= CAN_EFF_FLAG; ++ else ++ cf->can_id &= (~CAN_EFF_FLAG); ++ } else { ++ cf->can_id = can_id; ++ if (control & CAN_FD_SET_IDE_MASK) ++ cf->can_id |= CAN_EFF_FLAG; ++ else ++ cf->can_id &= (~CAN_EFF_FLAG); ++ ++ if (control & CAN_FD_SET_RTR_MASK) ++ cf->can_id |= CAN_RTR_FLAG; ++ } ++ ++ /* CANFD frames handed over to SKB */ ++ if (control & CAN_FD_SET_EDL_MASK) { ++ for (i = 0; i < cf->len; i += 4) ++ *((u32 *)(cf->data + i)) = priv->read_reg(priv, CANFD_RBUF_DATA_OFFSET + i); ++ } else { ++ /* skb reads the received datas, if the RTR bit not set */ ++ if (!(control & CAN_FD_SET_RTR_MASK)) { ++ *((u32 *)(cf->data + 0)) = priv->read_reg(priv, CANFD_RBUF_DATA_OFFSET); ++ *((u32 *)(cf->data + 4)) = priv->read_reg(priv, CANFD_RBUF_DATA_OFFSET + 4); ++ } ++ } ++ ++ canfd_reigister_set_bit(priv, CANFD_RCTRL_OFFSET, CAN_FD_SET_RREL_MASK); ++ ++ stats->rx_bytes += cf->len; ++ stats->rx_packets++; ++ netif_receive_skb(skb); ++ ++ return 1; ++} ++ ++static int canfd_rx_poll(struct napi_struct *napi, int quota) ++{ ++ struct net_device *ndev = napi->dev; ++ struct ipms_canfd_priv *priv = netdev_priv(ndev); ++ int work_done = 0; ++ u8 rx_status = 0, control = 0; ++ ++ control = can_ioread8(priv->reg_base + CANFD_RBUF_CTL_OFFSET); ++ rx_status = can_ioread8(priv->reg_base + CANFD_RCTRL_OFFSET); ++ ++ /* clear receive interrupt and deal with all the received frames */ ++ while ((rx_status & CAN_FD_RSTAT_NOT_EMPTY_MASK) && (work_done < quota)) { ++ (control & CAN_FD_SET_EDL_MASK) ? (work_done += canfd_rx(ndev)) : (work_done += can_rx(ndev)); ++ ++ control = can_ioread8(priv->reg_base + CANFD_RBUF_CTL_OFFSET); ++ rx_status = can_ioread8(priv->reg_base + CANFD_RCTRL_OFFSET); ++ } ++ napi_complete(napi); ++ canfd_reigister_set_bit(priv, CANFD_RTIE_OFFSET, CAN_FD_SET_RIE_MASK); ++ return work_done; ++} ++ ++static void canfd_rxfull_interrupt(struct net_device *ndev, u8 isr) ++{ ++ struct ipms_canfd_priv *priv = netdev_priv(ndev); ++ ++ if (isr & CAN_FD_SET_RAFIF_MASK) ++ canfd_reigister_set_bit(priv, CANFD_RTIF_OFFSET, CAN_FD_SET_RAFIF_MASK); ++ ++ if (isr & (CAN_FD_SET_RAFIF_MASK | CAN_FD_SET_RFIF_MASK)) ++ canfd_reigister_set_bit(priv, CANFD_RTIF_OFFSET, ++ (CAN_FD_SET_RAFIF_MASK | CAN_FD_SET_RFIF_MASK)); ++} ++ ++static int set_canfd_xmit_mode(struct net_device *ndev) ++{ ++ struct ipms_canfd_priv *priv = netdev_priv(ndev); ++ ++ switch (priv->tx_mode) { ++ case XMIT_FULL: ++ canfd_reigister_set_bit(priv, CANFD_TCTRL_OFFSET, CAN_FD_SET_FULLCAN_MASK); ++ break; ++ case XMIT_SEP_FIFO: ++ canfd_reigister_off_bit(priv, CANFD_TCTRL_OFFSET, CAN_FD_OFF_FULLCAN_MASK); ++ canfd_reigister_set_bit(priv, CANFD_TCTRL_OFFSET, CAN_FD_SET_FIFO_MASK); ++ canfd_reigister_off_bit(priv, CANFD_TCMD_OFFSET, CAN_FD_SET_TBSEL_MASK); ++ break; ++ case XMIT_SEP_PRIO: ++ canfd_reigister_off_bit(priv, CANFD_TCTRL_OFFSET, CAN_FD_OFF_FULLCAN_MASK); ++ canfd_reigister_off_bit(priv, CANFD_TCTRL_OFFSET, CAN_FD_OFF_FIFO_MASK); ++ canfd_reigister_off_bit(priv, CANFD_TCMD_OFFSET, CAN_FD_SET_TBSEL_MASK); ++ break; ++ case XMIT_PTB_MODE: ++ canfd_reigister_off_bit(priv, CANFD_TCMD_OFFSET, CAN_FD_OFF_TBSEL_MASK); ++ break; ++ default: ++ break; ++ } ++ return 0; ++} ++ ++static netdev_tx_t canfd_driver_start_xmit(struct sk_buff *skb, struct net_device *ndev) ++{ ++ struct ipms_canfd_priv *priv = netdev_priv(ndev); ++ struct canfd_frame *cf = (struct canfd_frame *)skb->data; ++ struct net_device_stats *stats = &ndev->stats; ++ u32 ttsen, id, ctl, addr_off; ++ int i; ++ ++ priv->tx_mode = XMIT_PTB_MODE; ++ ++ if (can_dropped_invalid_skb(ndev, skb)) ++ return NETDEV_TX_OK; ++ ++ switch (priv->tx_mode) { ++ case XMIT_FULL: ++ return NETDEV_TX_BUSY; ++ case XMIT_PTB_MODE: ++ set_canfd_xmit_mode(ndev); ++ canfd_reigister_off_bit(priv, CANFD_TCMD_OFFSET, CAN_FD_OFF_STBY_MASK); ++ ++ if (cf->can_id & CAN_EFF_FLAG) { ++ id = (cf->can_id & CAN_EFF_MASK); ++ ttsen = 0 << TTSEN_8_32_SHIFT; ++ id |= ttsen; ++ } else { ++ id = (cf->can_id & CAN_SFF_MASK); ++ ttsen = 0 << TTSEN_8_32_SHIFT; ++ id |= ttsen; ++ } ++ ++ ctl = can_fd_len2dlc(cf->len); ++ ++ /* transmit can fd frame */ ++ if (priv->can_or_canfd == IPMS_CAN_TYPE_CANFD) { ++ if (can_is_canfd_skb(skb)) { ++ if (cf->can_id & CAN_EFF_FLAG) ++ ctl |= CAN_FD_SET_IDE_MASK; ++ else ++ ctl &= CAN_FD_OFF_IDE_MASK; ++ ++ if (cf->flags & CANFD_BRS) ++ ctl |= CAN_FD_SET_BRS_MASK; ++ ++ ctl |= CAN_FD_SET_EDL_MASK; ++ ++ addr_off = CANFD_TBUF_DATA_OFFSET; ++ ++ for (i = 0; i < cf->len; i += 4) { ++ priv->write_reg(priv, addr_off, ++ *((u32 *)(cf->data + i))); ++ addr_off += 4; ++ } ++ } else { ++ ctl &= CAN_FD_OFF_EDL_MASK; ++ ctl &= CAN_FD_OFF_BRS_MASK; ++ ++ if (cf->can_id & CAN_EFF_FLAG) ++ ctl |= CAN_FD_SET_IDE_MASK; ++ else ++ ctl &= CAN_FD_OFF_IDE_MASK; ++ ++ if (cf->can_id & CAN_RTR_FLAG) { ++ ctl |= CAN_FD_SET_RTR_MASK; ++ priv->write_reg(priv, ++ CANFD_TBUF_ID_OFFSET, id); ++ priv->write_reg(priv, ++ CANFD_TBUF_CTL_OFFSET, ctl); ++ } else { ++ ctl &= CAN_FD_OFF_RTR_MASK; ++ addr_off = CANFD_TBUF_DATA_OFFSET; ++ priv->write_reg(priv, addr_off, ++ *((u32 *)(cf->data + 0))); ++ priv->write_reg(priv, addr_off + 4, ++ *((u32 *)(cf->data + 4))); ++ } ++ } ++ priv->write_reg(priv, CANFD_TBUF_ID_OFFSET, id); ++ priv->write_reg(priv, CANFD_TBUF_CTL_OFFSET, ctl); ++ addr_off = CANFD_TBUF_DATA_OFFSET; ++ } else { ++ ctl &= CAN_FD_OFF_EDL_MASK; ++ ctl &= CAN_FD_OFF_BRS_MASK; ++ ++ if (cf->can_id & CAN_EFF_FLAG) ++ ctl |= CAN_FD_SET_IDE_MASK; ++ else ++ ctl &= CAN_FD_OFF_IDE_MASK; ++ ++ if (cf->can_id & CAN_RTR_FLAG) { ++ ctl |= CAN_FD_SET_RTR_MASK; ++ priv->write_reg(priv, CANFD_TBUF_ID_OFFSET, id); ++ priv->write_reg(priv, CANFD_TBUF_CTL_OFFSET, ctl); ++ } else { ++ ctl &= CAN_FD_OFF_RTR_MASK; ++ priv->write_reg(priv, CANFD_TBUF_ID_OFFSET, id); ++ priv->write_reg(priv, CANFD_TBUF_CTL_OFFSET, ctl); ++ addr_off = CANFD_TBUF_DATA_OFFSET; ++ priv->write_reg(priv, addr_off, ++ *((u32 *)(cf->data + 0))); ++ priv->write_reg(priv, addr_off + 4, ++ *((u32 *)(cf->data + 4))); ++ } ++ } ++ canfd_reigister_set_bit(priv, CANFD_TCMD_OFFSET, CAN_FD_SET_TPE_MASK); ++ stats->tx_bytes += cf->len; ++ break; ++ default: ++ break; ++ } ++ ++ /*Due to cache blocking, we need call dev_kfree_skb() here to free the socket ++ buffer and return NETDEV_TX_OK */ ++ dev_kfree_skb(skb); ++ ++ return NETDEV_TX_OK; ++} ++ ++static int set_reset_mode(struct net_device *ndev) ++{ ++ struct ipms_canfd_priv *priv = netdev_priv(ndev); ++ u8 ret; ++ ++ ret = can_ioread8(priv->reg_base + CANFD_CFG_STAT_OFFSET); ++ ret |= CAN_FD_SET_RST_MASK; ++ can_iowrite8(ret, priv->reg_base + CANFD_CFG_STAT_OFFSET); ++ ++ return 0; ++} ++ ++static void canfd_driver_stop(struct net_device *ndev) ++{ ++ struct ipms_canfd_priv *priv = netdev_priv(ndev); ++ int ret; ++ ++ ret = set_reset_mode(ndev); ++ if (ret) ++ netdev_err(ndev, "Mode Resetting Failed!\n"); ++ ++ priv->can.state = CAN_STATE_STOPPED; ++} ++ ++static int canfd_driver_close(struct net_device *ndev) ++{ ++ struct ipms_canfd_priv *priv = netdev_priv(ndev); ++ ++ netif_stop_queue(ndev); ++ napi_disable(&priv->napi); ++ canfd_driver_stop(ndev); ++ ++ free_irq(ndev->irq, ndev); ++ close_candev(ndev); ++ ++ pm_runtime_put(priv->dev); ++ ++ return 0; ++} ++ ++static enum can_state get_of_chip_status(struct net_device *ndev) ++{ ++ struct ipms_canfd_priv *priv = netdev_priv(ndev); ++ u8 can_stat, eir; ++ ++ can_stat = can_ioread8(priv->reg_base + CANFD_CFG_STAT_OFFSET); ++ eir = can_ioread8(priv->reg_base + CANFD_ERRINT_OFFSET); ++ ++ if (can_stat & CAN_FD_SET_BUSOFF_MASK) ++ return CAN_STATE_BUS_OFF; ++ ++ if ((eir & CAN_FD_SET_EPASS_MASK) && ~(can_stat & CAN_FD_SET_BUSOFF_MASK)) ++ return CAN_STATE_ERROR_PASSIVE; ++ ++ if (eir & CAN_FD_SET_EWARN_MASK && ~(eir & CAN_FD_SET_EPASS_MASK)) ++ return CAN_STATE_ERROR_WARNING; ++ ++ if (~(eir & CAN_FD_SET_EPASS_MASK)) ++ return CAN_STATE_ERROR_ACTIVE; ++ ++ return CAN_STATE_ERROR_ACTIVE; ++} ++ ++static void canfd_error_interrupt(struct net_device *ndev, u8 isr, u8 eir) ++{ ++ struct ipms_canfd_priv *priv = netdev_priv(ndev); ++ struct net_device_stats *stats = &ndev->stats; ++ struct can_frame *cf; ++ struct sk_buff *skb; ++ u8 koer, recnt = 0, tecnt = 0, can_stat = 0; ++ ++ skb = alloc_can_err_skb(ndev, &cf); ++ ++ koer = can_ioread8(priv->reg_base + CANFD_EALCAP_OFFSET) & CAN_FD_SET_KOER_MASK; ++ recnt = can_ioread8(priv->reg_base + CANFD_RECNT_OFFSET); ++ tecnt = can_ioread8(priv->reg_base + CANFD_TECNT_OFFSET); ++ ++ /*Read can status*/ ++ can_stat = can_ioread8(priv->reg_base + CANFD_CFG_STAT_OFFSET); ++ ++ /* Bus off --->active error mode */ ++ if ((isr & CAN_FD_SET_EIF_MASK) && priv->can.state == CAN_STATE_BUS_OFF) ++ priv->can.state = get_of_chip_status(ndev); ++ ++ /* State selection */ ++ if (can_stat & CAN_FD_SET_BUSOFF_MASK) { ++ priv->can.state = get_of_chip_status(ndev); ++ priv->can.can_stats.bus_off++; ++ canfd_reigister_set_bit(priv, CANFD_CFG_STAT_OFFSET, CAN_FD_SET_BUSOFF_MASK); ++ can_bus_off(ndev); ++ if (skb) ++ cf->can_id |= CAN_ERR_BUSOFF; ++ ++ } else if ((eir & CAN_FD_SET_EPASS_MASK) && ~(can_stat & CAN_FD_SET_BUSOFF_MASK)) { ++ priv->can.state = get_of_chip_status(ndev); ++ priv->can.can_stats.error_passive++; ++ if (skb) { ++ cf->can_id |= CAN_ERR_CRTL; ++ cf->data[1] |= (recnt > 127) ? CAN_ERR_CRTL_RX_PASSIVE : 0; ++ cf->data[1] |= (tecnt > 127) ? CAN_ERR_CRTL_TX_PASSIVE : 0; ++ cf->data[6] = tecnt; ++ cf->data[7] = recnt; ++ } ++ } else if (eir & CAN_FD_SET_EWARN_MASK && ~(eir & CAN_FD_SET_EPASS_MASK)) { ++ priv->can.state = get_of_chip_status(ndev); ++ priv->can.can_stats.error_warning++; ++ if (skb) { ++ cf->can_id |= CAN_ERR_CRTL; ++ cf->data[1] |= (recnt > 95) ? CAN_ERR_CRTL_RX_WARNING : 0; ++ cf->data[1] |= (tecnt > 95) ? CAN_ERR_CRTL_TX_WARNING : 0; ++ cf->data[6] = tecnt; ++ cf->data[7] = recnt; ++ } ++ } ++ ++ /* Check for in protocol defined error interrupt */ ++ if (eir & CAN_FD_SET_BEIF_MASK) { ++ if (skb) ++ cf->can_id |= CAN_ERR_BUSERROR | CAN_ERR_PROT; ++ ++ /* bit error interrupt */ ++ if (koer == CAN_FD_SET_BIT_ERROR_MASK) { ++ stats->tx_errors++; ++ if (skb) { ++ cf->can_id |= CAN_ERR_PROT; ++ cf->data[2] = CAN_ERR_PROT_BIT; ++ } ++ } ++ /* format error interrupt */ ++ if (koer == CAN_FD_SET_FORM_ERROR_MASK) { ++ stats->rx_errors++; ++ if (skb) { ++ cf->can_id |= CAN_ERR_PROT; ++ cf->data[2] = CAN_ERR_PROT_FORM; ++ } ++ } ++ /* stuffing error interrupt */ ++ if (koer == CAN_FD_SET_STUFF_ERROR_MASK) { ++ stats->rx_errors++; ++ if (skb) { ++ cf->can_id |= CAN_ERR_PROT; ++ cf->data[3] = CAN_ERR_PROT_STUFF; ++ } ++ } ++ /* ack error interrupt */ ++ if (koer == CAN_FD_SET_ACK_ERROR_MASK) { ++ stats->tx_errors++; ++ if (skb) { ++ cf->can_id |= CAN_ERR_PROT; ++ cf->data[2] = CAN_ERR_PROT_LOC_ACK; ++ } ++ } ++ /* crc error interrupt */ ++ if (koer == CAN_FD_SET_CRC_ERROR_MASK) { ++ stats->rx_errors++; ++ if (skb) { ++ cf->can_id |= CAN_ERR_PROT; ++ cf->data[2] = CAN_ERR_PROT_LOC_CRC_SEQ; ++ } ++ } ++ priv->can.can_stats.bus_error++; ++ } ++ if (skb) { ++ stats->rx_packets++; ++ stats->rx_bytes += cf->can_dlc; ++ netif_rx(skb); ++ } ++ ++ netdev_dbg(ndev, "Recnt is 0x%02x", can_ioread8(priv->reg_base + CANFD_RECNT_OFFSET)); ++ netdev_dbg(ndev, "Tecnt is 0x%02x", can_ioread8(priv->reg_base + CANFD_TECNT_OFFSET)); ++} ++ ++static irqreturn_t canfd_interrupt(int irq, void *dev_id) ++{ ++ struct net_device *ndev = (struct net_device *)dev_id; ++ struct ipms_canfd_priv *priv = netdev_priv(ndev); ++ u8 isr, eir; ++ u8 isr_handled = 0, eir_handled = 0; ++ ++ /* read the value of interrupt status register */ ++ isr = can_ioread8(priv->reg_base + CANFD_RTIF_OFFSET); ++ ++ /* read the value of error interrupt register */ ++ eir = can_ioread8(priv->reg_base + CANFD_ERRINT_OFFSET); ++ ++ /* Check for Tx interrupt and Processing it */ ++ if (isr & (CAN_FD_SET_TPIF_MASK | CAN_FD_SET_TSIF_MASK)) { ++ canfd_tx_interrupt(ndev, isr); ++ isr_handled |= (CAN_FD_SET_TPIF_MASK | CAN_FD_SET_TSIF_MASK); ++ } ++ if (isr & (CAN_FD_SET_RAFIF_MASK | CAN_FD_SET_RFIF_MASK)) { ++ canfd_rxfull_interrupt(ndev, isr); ++ isr_handled |= (CAN_FD_SET_RAFIF_MASK | CAN_FD_SET_RFIF_MASK); ++ } ++ /* Check Rx interrupt and Processing the receive interrupt routine */ ++ if (isr & CAN_FD_SET_RIF_MASK) { ++ canfd_reigister_off_bit(priv, CANFD_RTIE_OFFSET, CAN_FD_OFF_RIE_MASK); ++ canfd_reigister_set_bit(priv, CANFD_RTIF_OFFSET, CAN_FD_SET_RIF_MASK); ++ ++ napi_schedule(&priv->napi); ++ isr_handled |= CAN_FD_SET_RIF_MASK; ++ } ++ if ((isr & CAN_FD_SET_EIF_MASK) | (eir & (CAN_FD_SET_EPIF_MASK | CAN_FD_SET_BEIF_MASK))) { ++ /* reset EPIF and BEIF. Reset EIF */ ++ canfd_reigister_set_bit(priv, CANFD_ERRINT_OFFSET, ++ eir & (CAN_FD_SET_EPIF_MASK | CAN_FD_SET_BEIF_MASK)); ++ canfd_reigister_set_bit(priv, CANFD_RTIF_OFFSET, ++ isr & CAN_FD_SET_EIF_MASK); ++ ++ canfd_error_interrupt(ndev, isr, eir); ++ ++ isr_handled |= CAN_FD_SET_EIF_MASK; ++ eir_handled |= (CAN_FD_SET_EPIF_MASK | CAN_FD_SET_BEIF_MASK); ++ } ++ if ((isr_handled == 0) && (eir_handled == 0)) { ++ netdev_err(ndev, "Unhandled interrupt!\n"); ++ return IRQ_NONE; ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++static int canfd_chip_start(struct net_device *ndev) ++{ ++ struct ipms_canfd_priv *priv = netdev_priv(ndev); ++ int err; ++ u8 ret; ++ ++ err = set_reset_mode(ndev); ++ if (err) { ++ netdev_err(ndev, "Mode Resetting Failed!\n"); ++ return err; ++ } ++ ++ err = canfd_device_driver_bittime_configuration(ndev); ++ if (err) { ++ netdev_err(ndev, "Bittime Setting Failed!\n"); ++ return err; ++ } ++ ++ /* Set Almost Full Warning Limit */ ++ canfd_reigister_set_bit(priv, CANFD_LIMIT_OFFSET, CAN_FD_SET_AFWL_MASK); ++ ++ /* Programmable Error Warning Limit = (EWL+1)*8. Set EWL=11->Error Warning=96 */ ++ canfd_reigister_set_bit(priv, CANFD_LIMIT_OFFSET, CAN_FD_SET_EWL_MASK); ++ ++ /* Interrupts enable */ ++ can_iowrite8(CAN_FD_INTR_ALL_MASK, priv->reg_base + CANFD_RTIE_OFFSET); ++ ++ /* Error Interrupts enable(Error Passive and Bus Error) */ ++ canfd_reigister_set_bit(priv, CANFD_ERRINT_OFFSET, CAN_FD_SET_EPIE_MASK); ++ ++ ret = can_ioread8(priv->reg_base + CANFD_CFG_STAT_OFFSET); ++ ++ /* Check whether it is loopback mode or normal mode */ ++ if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) { ++ ret |= CAN_FD_LBMIMOD_MASK; ++ } else { ++ ret &= ~CAN_FD_LBMEMOD_MASK; ++ ret &= ~CAN_FD_LBMIMOD_MASK; ++ } ++ ++ can_iowrite8(ret, priv->reg_base + CANFD_CFG_STAT_OFFSET); ++ ++ priv->can.state = CAN_STATE_ERROR_ACTIVE; ++ ++ return 0; ++} ++ ++static int canfd_do_set_mode(struct net_device *ndev, enum can_mode mode) ++{ ++ int ret; ++ ++ switch (mode) { ++ case CAN_MODE_START: ++ ret = canfd_chip_start(ndev); ++ if (ret) { ++ netdev_err(ndev, "Could Not Start CAN device !!\n"); ++ return ret; ++ } ++ netif_wake_queue(ndev); ++ break; ++ default: ++ ret = -EOPNOTSUPP; ++ break; ++ } ++ ++ return ret; ++} ++ ++static int canfd_driver_open(struct net_device *ndev) ++{ ++ struct ipms_canfd_priv *priv = netdev_priv(ndev); ++ int ret; ++ ++ ret = pm_runtime_get_sync(priv->dev); ++ if (ret < 0) { ++ netdev_err(ndev, "%s: pm_runtime_get failed(%d)\n", ++ __func__, ret); ++ goto err; ++ } ++ ++ /* Set chip into reset mode */ ++ ret = set_reset_mode(ndev); ++ if (ret) { ++ netdev_err(ndev, "Mode Resetting Failed!\n"); ++ return ret; ++ } ++ ++ /* Common open */ ++ ret = open_candev(ndev); ++ if (ret) ++ return ret; ++ ++ /* Register interrupt handler */ ++ ret = request_irq(ndev->irq, canfd_interrupt, IRQF_SHARED, ndev->name, ndev); ++ if (ret) { ++ netdev_err(ndev, "Request_irq err: %d\n", ret); ++ goto exit_irq; ++ } ++ ++ ret = canfd_chip_start(ndev); ++ if (ret) { ++ netdev_err(ndev, "Could Not Start CAN device !\n"); ++ goto exit_can_start; ++ } ++ ++ napi_enable(&priv->napi); ++ netif_start_queue(ndev); ++ ++ return 0; ++ ++exit_can_start: ++ free_irq(ndev->irq, ndev); ++err: ++ pm_runtime_put(priv->dev); ++exit_irq: ++ close_candev(ndev); ++ return ret; ++} ++ ++static int canfd_control_parse_dt(struct ipms_canfd_priv *priv) ++{ ++ struct of_phandle_args args; ++ u32 syscon_mask, syscon_shift; ++ u32 can_or_canfd; ++ u32 syscon_offset, regval; ++ int ret; ++ ++ ret = of_parse_phandle_with_fixed_args(priv->dev->of_node, ++ "starfive,sys-syscon", 3, 0, &args); ++ if (ret) { ++ dev_err(priv->dev, "Failed to parse starfive,sys-syscon\n"); ++ return -EINVAL; ++ } ++ ++ priv->reg_syscon = syscon_node_to_regmap(args.np); ++ of_node_put(args.np); ++ if (IS_ERR(priv->reg_syscon)) ++ return PTR_ERR(priv->reg_syscon); ++ ++ syscon_offset = args.args[0]; ++ syscon_shift = args.args[1]; ++ syscon_mask = args.args[2]; ++ ++ ret = device_property_read_u32(priv->dev, "syscon,can_or_canfd", &can_or_canfd); ++ if (ret) ++ goto exit_parse; ++ ++ priv->can_or_canfd = can_or_canfd; ++ ++ /* enable can2.0/canfd function */ ++ regval = can_or_canfd << syscon_shift; ++ ret = regmap_update_bits(priv->reg_syscon, syscon_offset, syscon_mask, regval); ++ if (ret) ++ return ret; ++ return 0; ++exit_parse: ++ return ret; ++} ++ ++static const struct net_device_ops canfd_netdev_ops = { ++ .ndo_open = canfd_driver_open, ++ .ndo_stop = canfd_driver_close, ++ .ndo_start_xmit = canfd_driver_start_xmit, ++ .ndo_change_mtu = can_change_mtu, ++}; ++ ++static int canfd_driver_probe(struct platform_device *pdev) ++{ ++ struct net_device *ndev; ++ struct ipms_canfd_priv *priv; ++ void __iomem *addr; ++ int ret; ++ u32 frq; ++ ++ addr = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(addr)) { ++ ret = PTR_ERR(addr); ++ goto exit; ++ } ++ ++ ndev = alloc_candev(sizeof(struct ipms_canfd_priv), 1); ++ if (!ndev) { ++ ret = -ENOMEM; ++ goto exit; ++ } ++ ++ priv = netdev_priv(ndev); ++ priv->dev = &pdev->dev; ++ ++ ret = canfd_control_parse_dt(priv); ++ if (ret) ++ goto free_exit; ++ ++ priv->nr_clks = devm_clk_bulk_get_all(priv->dev, &priv->clks); ++ if (priv->nr_clks < 0) { ++ dev_err(priv->dev, "Failed to get can clocks\n"); ++ ret = -ENODEV; ++ goto free_exit; ++ } ++ ++ ret = clk_bulk_prepare_enable(priv->nr_clks, priv->clks); ++ if (ret) { ++ dev_err(priv->dev, "Failed to enable clocks\n"); ++ goto free_exit; ++ } ++ ++ priv->resets = devm_reset_control_array_get_exclusive(priv->dev); ++ if (IS_ERR(priv->resets)) { ++ ret = PTR_ERR(priv->resets); ++ dev_err(priv->dev, "Failed to get can resets"); ++ goto clk_exit; ++ } ++ ++ ret = reset_control_deassert(priv->resets); ++ if (ret) ++ goto clk_exit; ++ priv->can.bittiming_const = &canfd_bittiming_const; ++ priv->can.data_bittiming_const = &canfd_data_bittiming_const; ++ priv->can.do_set_mode = canfd_do_set_mode; ++ ++ /* in user space the execution mode can be chosen */ ++ if (priv->can_or_canfd == IPMS_CAN_TYPE_CANFD) ++ priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK | CAN_CTRLMODE_FD; ++ else ++ priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK; ++ priv->reg_base = addr; ++ priv->write_reg = canfd_write_reg_le; ++ priv->read_reg = canfd_read_reg_le; ++ ++ pm_runtime_enable(&pdev->dev); ++ ++ priv->can_clk = devm_clk_get(&pdev->dev, "core_clk"); ++ if (IS_ERR(priv->can_clk)) { ++ dev_err(&pdev->dev, "Device clock not found.\n"); ++ ret = PTR_ERR(priv->can_clk); ++ goto reset_exit; ++ } ++ ++ device_property_read_u32(priv->dev, "frequency", &frq); ++ clk_set_rate(priv->can_clk, frq); ++ ++ priv->can.clock.freq = clk_get_rate(priv->can_clk); ++ ndev->irq = platform_get_irq(pdev, 0); ++ ++ /* we support local echo */ ++ ndev->flags |= IFF_ECHO; ++ ndev->netdev_ops = &canfd_netdev_ops; ++ ++ platform_set_drvdata(pdev, ndev); ++ SET_NETDEV_DEV(ndev, &pdev->dev); ++ ++ netif_napi_add(ndev, &priv->napi, canfd_rx_poll); ++ ret = register_candev(ndev); ++ if (ret) { ++ dev_err(&pdev->dev, "Fail to register failed (err=%d)\n", ret); ++ goto reset_exit; ++ } ++ ++ dev_dbg(&pdev->dev, "Driver registered: regs=%p, irp=%d, clock=%d\n", ++ priv->reg_base, ndev->irq, priv->can.clock.freq); ++ ++ return 0; ++ ++reset_exit: ++ reset_control_assert(priv->resets); ++clk_exit: ++ clk_bulk_disable_unprepare(priv->nr_clks, priv->clks); ++free_exit: ++ free_candev(ndev); ++exit: ++ return ret; ++} ++ ++static int canfd_driver_remove(struct platform_device *pdev) ++{ ++ struct net_device *ndev = platform_get_drvdata(pdev); ++ struct ipms_canfd_priv *priv = netdev_priv(ndev); ++ ++ reset_control_assert(priv->resets); ++ clk_bulk_disable_unprepare(priv->nr_clks, priv->clks); ++ pm_runtime_disable(&pdev->dev); ++ ++ unregister_candev(ndev); ++ netif_napi_del(&priv->napi); ++ free_candev(ndev); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM_SLEEP ++static int __maybe_unused canfd_suspend(struct device *dev) ++{ ++ struct net_device *ndev = dev_get_drvdata(dev); ++ ++ if (netif_running(ndev)) { ++ netif_stop_queue(ndev); ++ netif_device_detach(ndev); ++ canfd_driver_stop(ndev); ++ } ++ ++ return pm_runtime_force_suspend(dev); ++} ++ ++static int __maybe_unused canfd_resume(struct device *dev) ++{ ++ struct net_device *ndev = dev_get_drvdata(dev); ++ int ret; ++ ++ ret = pm_runtime_force_resume(dev); ++ if (ret) { ++ dev_err(dev, "pm_runtime_force_resume failed on resume\n"); ++ return ret; ++ } ++ ++ if (netif_running(ndev)) { ++ ret = canfd_chip_start(ndev); ++ if (ret) { ++ dev_err(dev, "canfd_chip_start failed on resume\n"); ++ return ret; ++ } ++ ++ netif_device_attach(ndev); ++ netif_start_queue(ndev); ++ } ++ ++ return 0; ++} ++#endif ++ ++#ifdef CONFIG_PM ++static int canfd_runtime_suspend(struct device *dev) ++{ ++ struct net_device *ndev = dev_get_drvdata(dev); ++ struct ipms_canfd_priv *priv = netdev_priv(ndev); ++ ++ reset_control_assert(priv->resets); ++ clk_bulk_disable_unprepare(priv->nr_clks, priv->clks); ++ ++ return 0; ++} ++ ++static int canfd_runtime_resume(struct device *dev) ++{ ++ struct net_device *ndev = dev_get_drvdata(dev); ++ struct ipms_canfd_priv *priv = netdev_priv(ndev); ++ int ret; ++ ++ ret = clk_bulk_prepare_enable(priv->nr_clks, priv->clks); ++ if (ret) { ++ dev_err(dev, "Failed to prepare_enable clk\n"); ++ return ret; ++ } ++ ++ ret = reset_control_deassert(priv->resets); ++ if (ret) { ++ dev_err(dev, "Failed to deassert reset\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++#endif ++ ++static const struct dev_pm_ops canfd_pm_ops = { ++ SET_SYSTEM_SLEEP_PM_OPS(canfd_suspend, canfd_resume) ++ SET_RUNTIME_PM_OPS(canfd_runtime_suspend, ++ canfd_runtime_resume, NULL) ++}; ++ ++static const struct of_device_id canfd_of_match[] = { ++ { .compatible = "ipms,can" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, canfd_of_match); ++ ++static struct platform_driver can_driver = { ++ .probe = canfd_driver_probe, ++ .remove = canfd_driver_remove, ++ .driver = { ++ .name = DRIVER_NAME, ++ .pm = &canfd_pm_ops, ++ .of_match_table = canfd_of_match, ++ }, ++}; ++ ++module_platform_driver(can_driver); ++ ++MODULE_DESCRIPTION("ipms can controller driver for StarFive jh7110 SoC"); ++MODULE_AUTHOR("William Qiu +Date: Fri, 9 Jun 2023 14:57:13 +0800 +Subject: [PATCH 071/116] regulator: starfive-jh7110: Add regulator support for + JH7110 A type EVB. + +Add 7 regulators base on regulator framework for +JH7110 evb HW design. + +Signed-off-by: Kevin.xie +--- + drivers/regulator/Kconfig | 10 ++ + drivers/regulator/Makefile | 1 + + drivers/regulator/starfive-jh7110-regulator.c | 126 ++++++++++++++++++ + include/linux/regulator/jh7110.h | 24 ++++ + 4 files changed, 161 insertions(+) + create mode 100644 drivers/regulator/starfive-jh7110-regulator.c + create mode 100644 include/linux/regulator/jh7110.h + +--- a/drivers/regulator/Kconfig ++++ b/drivers/regulator/Kconfig +@@ -1335,6 +1335,16 @@ config REGULATOR_SM5703 + This driver provides support for voltage regulators of SM5703 + multi-function device. + ++config REGULATOR_STARFIVE_JH7110 ++ tristate "Starfive JH7110 PMIC" ++ depends on I2C ++ select REGMAP_I2C ++ help ++ Say y here to select this option to enable the power regulator of ++ Starfive JH7110 PMIC. ++ This driver supports the control of different power rails of device ++ through regulator interface. ++ + config REGULATOR_STM32_BOOSTER + tristate "STMicroelectronics STM32 BOOSTER" + depends on ARCH_STM32 || COMPILE_TEST +--- a/drivers/regulator/Makefile ++++ b/drivers/regulator/Makefile +@@ -156,6 +156,7 @@ obj-$(CONFIG_REGULATOR_SC2731) += sc2731 + obj-$(CONFIG_REGULATOR_SKY81452) += sky81452-regulator.o + obj-$(CONFIG_REGULATOR_SLG51000) += slg51000-regulator.o + obj-$(CONFIG_REGULATOR_SM5703) += sm5703-regulator.o ++obj-$(CONFIG_REGULATOR_STARFIVE_JH7110) += starfive-jh7110-regulator.o + obj-$(CONFIG_REGULATOR_STM32_BOOSTER) += stm32-booster.o + obj-$(CONFIG_REGULATOR_STM32_VREFBUF) += stm32-vrefbuf.o + obj-$(CONFIG_REGULATOR_STM32_PWR) += stm32-pwr.o +--- /dev/null ++++ b/drivers/regulator/starfive-jh7110-regulator.c +@@ -0,0 +1,126 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2022 Starfive Technology Co., Ltd. ++ * Author: Mason Huo ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define JH7110_PM_POWER_SW_0 0x80 ++#define JH7110_PM_POWER_SW_1 0x81 ++#define ENABLE_MASK(id) BIT(id) ++ ++ ++static const struct regmap_config jh7110_regmap_config = { ++ .reg_bits = 8, ++ .val_bits = 8, ++ .max_register = JH7110_PM_POWER_SW_1, ++ .cache_type = REGCACHE_FLAT, ++}; ++ ++static const struct regulator_ops jh7110_ldo_ops = { ++ .enable = regulator_enable_regmap, ++ .disable = regulator_disable_regmap, ++ .is_enabled = regulator_is_enabled_regmap, ++}; ++ ++#define JH7110_LDO(_id, _name, en_reg, en_mask) \ ++{\ ++ .name = (_name),\ ++ .ops = &jh7110_ldo_ops,\ ++ .of_match = of_match_ptr(_name),\ ++ .regulators_node = of_match_ptr("regulators"),\ ++ .type = REGULATOR_VOLTAGE,\ ++ .id = JH7110_ID_##_id,\ ++ .owner = THIS_MODULE,\ ++ .enable_reg = JH7110_PM_POWER_SW_##en_reg,\ ++ .enable_mask = ENABLE_MASK(en_mask),\ ++} ++ ++static const struct regulator_desc jh7110_regulators[] = { ++ JH7110_LDO(LDO_REG1, "hdmi_1p8", 0, 0), ++ JH7110_LDO(LDO_REG2, "mipitx_1p8", 0, 1), ++ JH7110_LDO(LDO_REG3, "mipirx_1p8", 0, 2), ++ JH7110_LDO(LDO_REG4, "hdmi_0p9", 0, 3), ++ JH7110_LDO(LDO_REG5, "mipitx_0p9", 0, 4), ++ JH7110_LDO(LDO_REG6, "mipirx_0p9", 0, 5), ++ JH7110_LDO(LDO_REG7, "sdio_vdd", 1, 0), ++}; ++ ++static int jh7110_i2c_probe(struct i2c_client *i2c) ++{ ++ struct regulator_config config = { }; ++ struct regulator_dev *rdev; ++ struct regulator_init_data *init_data; ++ struct regmap *regmap; ++ int i, ret; ++ ++ regmap = devm_regmap_init_i2c(i2c, &jh7110_regmap_config); ++ if (IS_ERR(regmap)) { ++ ret = PTR_ERR(regmap); ++ dev_err(&i2c->dev, "Failed to allocate register map: %d\n", ++ ret); ++ return ret; ++ } ++ ++ init_data = of_get_regulator_init_data(&i2c->dev, i2c->dev.of_node, NULL); ++ if (!init_data) ++ return -ENOMEM; ++ config.init_data = init_data; ++ ++ for (i = 0; i < JH7110_MAX_REGULATORS; i++) { ++ config.dev = &i2c->dev; ++ config.regmap = regmap; ++ ++ rdev = devm_regulator_register(&i2c->dev, ++ &jh7110_regulators[i], &config); ++ if (IS_ERR(rdev)) { ++ dev_err(&i2c->dev, ++ "Failed to register JH7110 regulator\n"); ++ return PTR_ERR(rdev); ++ } ++ } ++ ++ return 0; ++} ++ ++static const struct i2c_device_id jh7110_i2c_id[] = { ++ {"jh7110_evb_reg", 0}, ++ {}, ++}; ++MODULE_DEVICE_TABLE(i2c, jh7110_i2c_id); ++ ++#ifdef CONFIG_OF ++static const struct of_device_id jh7110_dt_ids[] = { ++ { .compatible = "starfive,jh7110-evb-regulator", ++ .data = &jh7110_i2c_id[0] }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, jh7110_dt_ids); ++#endif ++ ++static struct i2c_driver jh7110_regulator_driver = { ++ .driver = { ++ .name = "jh7110-evb-regulator", ++ .of_match_table = of_match_ptr(jh7110_dt_ids), ++ }, ++ .probe = jh7110_i2c_probe, ++ .id_table = jh7110_i2c_id, ++}; ++ ++module_i2c_driver(jh7110_regulator_driver); ++ ++MODULE_AUTHOR("Mason Huo "); ++MODULE_DESCRIPTION("Regulator device driver for Starfive JH7110"); ++MODULE_LICENSE("GPL v2"); +--- /dev/null ++++ b/include/linux/regulator/jh7110.h +@@ -0,0 +1,24 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++/* ++ * Copyright (c) 2022 Starfive Technology Co., Ltd. ++ * Author: Mason Huo ++ */ ++ ++#ifndef __LINUX_REGULATOR_JH7110_H ++#define __LINUX_REGULATOR_JH7110_H ++ ++#define JH7110_MAX_REGULATORS 7 ++ ++ ++enum jh7110_reg_id { ++ JH7110_ID_LDO_REG1 = 0, ++ JH7110_ID_LDO_REG2, ++ JH7110_ID_LDO_REG3, ++ JH7110_ID_LDO_REG4, ++ JH7110_ID_LDO_REG5, ++ JH7110_ID_LDO_REG6, ++ JH7110_ID_LDO_REG7, ++}; ++ ++ ++#endif /* __LINUX_REGULATOR_JH7110_H */ diff --git a/target/linux/starfive/patches-6.6/0072-drivers-nvme-Add-precheck-and-delay-for-CQE-pending-.patch b/target/linux/starfive/patches-6.6/0072-drivers-nvme-Add-precheck-and-delay-for-CQE-pending-.patch new file mode 100644 index 0000000000..d602df9e4c --- /dev/null +++ b/target/linux/starfive/patches-6.6/0072-drivers-nvme-Add-precheck-and-delay-for-CQE-pending-.patch @@ -0,0 +1,40 @@ +From f0b4cffe4d1813305f783d208f260747ecc56c50 Mon Sep 17 00:00:00 2001 +From: "Kevin.xie" +Date: Thu, 24 Nov 2022 16:59:12 +0800 +Subject: [PATCH 072/116] drivers: nvme: Add precheck and delay for CQE pending + status. + +To workaroud the NVMe I/O timeout problem in bootup S10udev case +which caused by the CQE update lantancy. + +Signed-off-by: Kevin.xie +--- + drivers/nvme/host/pci.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +--- a/drivers/nvme/host/pci.c ++++ b/drivers/nvme/host/pci.c +@@ -28,6 +28,7 @@ + #include + #include + #include ++#include + + #include "trace.h" + #include "nvme.h" +@@ -1062,6 +1063,15 @@ static inline int nvme_poll_cq(struct nv + { + int found = 0; + ++ /* ++ * In some cases, such as udev trigger, cqe status may update ++ * a little bit later than MSI, which cause an irq handle missing. ++ * To workaound, here we will prefetch the status first, and wait ++ * 1us if we get nothing. ++ */ ++ if (!nvme_cqe_pending(nvmeq)) ++ udelay(1); ++ + while (nvme_cqe_pending(nvmeq)) { + found++; + /* diff --git a/target/linux/starfive/patches-6.6/0073-RISC-V-Create-unique-identification-for-SoC-PMU.patch b/target/linux/starfive/patches-6.6/0073-RISC-V-Create-unique-identification-for-SoC-PMU.patch new file mode 100644 index 0000000000..fff01c4c8f --- /dev/null +++ b/target/linux/starfive/patches-6.6/0073-RISC-V-Create-unique-identification-for-SoC-PMU.patch @@ -0,0 +1,93 @@ +From eb294df4b9fab46bc5dbf676edf51e28e06d1968 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jo=C3=A3o=20M=C3=A1rio=20Domingos?= + +Date: Tue, 16 Nov 2021 15:48:09 +0000 +Subject: [PATCH 073/116] RISC-V: Create unique identification for SoC PMU +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The SBI PMU platform driver did not provide any identification for +perf events matching. This patch introduces a new sysfs file inside the +platform device (soc:pmu/id) for pmu identification. + +The identification is a 64-bit value generated as: +[63-32]: mvendorid; +[31]: marchid[MSB]; +[30-16]: marchid[15-0]; +[15-0]: mimpid[15MSBs]; + +The CSRs are detailed in the RISC-V privileged spec [1]. +The marchid is split in MSB + 15LSBs, due to the MSB being used for +open-source architecture identification. + +[1] https://github.com/riscv/riscv-isa-manual + +Signed-off-by: João Mário Domingos +--- + drivers/perf/riscv_pmu_sbi.c | 47 ++++++++++++++++++++++++++++++++++++ + 1 file changed, 47 insertions(+) + +--- a/drivers/perf/riscv_pmu_sbi.c ++++ b/drivers/perf/riscv_pmu_sbi.c +@@ -1019,6 +1019,46 @@ static struct ctl_table sbi_pmu_sysctl_t + { } + }; + ++static uint64_t pmu_sbi_get_pmu_id(void) ++{ ++ union sbi_pmu_id { ++ uint64_t value; ++ struct { ++ uint16_t imp:16; ++ uint16_t arch:16; ++ uint32_t vendor:32; ++ }; ++ } pmuid; ++ ++ pmuid.value = 0; ++ pmuid.vendor = (uint32_t) sbi_get_mvendorid(); ++ pmuid.arch = (sbi_get_marchid() >> (63 - 15) & (1 << 15)) | (sbi_get_marchid() & 0x7FFF); ++ pmuid.imp = (sbi_get_mimpid() >> 16); ++ ++ return pmuid.value; ++} ++ ++static ssize_t pmu_sbi_id_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ int len; ++ ++ len = sprintf(buf, "0x%llx\n", pmu_sbi_get_pmu_id()); ++ if (len <= 0) ++ dev_err(dev, "mydrv: Invalid sprintf len: %dn", len); ++ ++ return len; ++} ++ ++static DEVICE_ATTR(id, S_IRUGO | S_IWUSR, pmu_sbi_id_show, 0); ++ ++static struct attribute *pmu_sbi_attrs[] = { ++ &dev_attr_id.attr, ++ NULL ++}; ++ ++ATTRIBUTE_GROUPS(pmu_sbi); ++ + static int pmu_sbi_device_probe(struct platform_device *pdev) + { + struct riscv_pmu *pmu = NULL; +@@ -1067,6 +1107,13 @@ static int pmu_sbi_device_probe(struct p + pmu->event_unmapped = pmu_sbi_event_unmapped; + pmu->csr_index = pmu_sbi_csr_index; + ++ ret = sysfs_create_group(&pdev->dev.kobj, &pmu_sbi_group); ++ if (ret) { ++ dev_err(&pdev->dev, "sysfs creation failed\n"); ++ return ret; ++ } ++ pdev->dev.groups = pmu_sbi_groups; ++ + ret = cpuhp_state_add_instance(CPUHP_AP_PERF_RISCV_STARTING, &pmu->node); + if (ret) + return ret; diff --git a/target/linux/starfive/patches-6.6/0074-RISC-V-Support-CPUID-for-risc-v-in-perf.patch b/target/linux/starfive/patches-6.6/0074-RISC-V-Support-CPUID-for-risc-v-in-perf.patch new file mode 100644 index 0000000000..177b3b32d8 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0074-RISC-V-Support-CPUID-for-risc-v-in-perf.patch @@ -0,0 +1,55 @@ +From 1dc069ffadf4ce7817a716f9df2f480254e9b01d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jo=C3=A3o=20M=C3=A1rio=20Domingos?= + +Date: Tue, 16 Nov 2021 15:48:10 +0000 +Subject: [PATCH 074/116] RISC-V: Support CPUID for risc-v in perf +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This patch creates the header.c file for the risc-v architecture and introduces support for +PMU identification through sysfs. +It is now possible to configure pmu-events in risc-v. + +Depends on patch [1], that introduces the id sysfs file. + +Signed-off-by: João Mário Domingos +Signed-off-by: minda.chen +--- + drivers/perf/riscv_pmu.c | 18 ++++++++++++++++++ + 1 file changed, 18 insertions(+) + +--- a/drivers/perf/riscv_pmu.c ++++ b/drivers/perf/riscv_pmu.c +@@ -18,6 +18,23 @@ + + #include + ++PMU_FORMAT_ATTR(event, "config:0-63"); ++ ++static struct attribute *riscv_arch_formats_attr[] = { ++ &format_attr_event.attr, ++ NULL, ++}; ++ ++static struct attribute_group riscv_pmu_format_group = { ++ .name = "format", ++ .attrs = riscv_arch_formats_attr, ++}; ++ ++static const struct attribute_group *riscv_pmu_attr_groups[] = { ++ &riscv_pmu_format_group, ++ NULL, ++}; ++ + static bool riscv_perf_user_access(struct perf_event *event) + { + return ((event->attr.type == PERF_TYPE_HARDWARE) || +@@ -410,6 +427,7 @@ struct riscv_pmu *riscv_pmu_alloc(void) + cpuc->events[i] = NULL; + } + pmu->pmu = (struct pmu) { ++ .attr_groups = riscv_pmu_attr_groups, + .event_init = riscv_pmu_event_init, + .event_mapped = riscv_pmu_event_mapped, + .event_unmapped = riscv_pmu_event_unmapped, diff --git a/target/linux/starfive/patches-6.6/0075-RISC-V-Added-generic-pmu-events-mapfile.patch b/target/linux/starfive/patches-6.6/0075-RISC-V-Added-generic-pmu-events-mapfile.patch new file mode 100644 index 0000000000..01bb4f19ca --- /dev/null +++ b/target/linux/starfive/patches-6.6/0075-RISC-V-Added-generic-pmu-events-mapfile.patch @@ -0,0 +1,42 @@ +From 3e6ea12dda276c01a756764fcafa315b19860c33 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jo=C3=A3o=20M=C3=A1rio=20Domingos?= + +Date: Tue, 16 Nov 2021 15:48:11 +0000 +Subject: [PATCH 075/116] RISC-V: Added generic pmu-events mapfile +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The pmu-events now supports custom events for RISC-V, plus the cycle, +time and instret events were defined. + +Signed-off-by: João Mário Domingos +--- + .../pmu-events/arch/riscv/riscv-generic.json | 20 +++++++++++++++++++ + 1 file changed, 20 insertions(+) + create mode 100644 tools/perf/pmu-events/arch/riscv/riscv-generic.json + +--- /dev/null ++++ b/tools/perf/pmu-events/arch/riscv/riscv-generic.json +@@ -0,0 +1,20 @@ ++[ ++ { ++ "PublicDescription": "CPU Cycles", ++ "EventCode": "0x00", ++ "EventName": "riscv_cycles", ++ "BriefDescription": "CPU cycles RISC-V generic counter" ++ }, ++ { ++ "PublicDescription": "CPU Time", ++ "EventCode": "0x01", ++ "EventName": "riscv_time", ++ "BriefDescription": "CPU time RISC-V generic counter" ++ }, ++ { ++ "PublicDescription": "CPU Instructions", ++ "EventCode": "0x02", ++ "EventName": "riscv_instret", ++ "BriefDescription": "CPU retired instructions RISC-V generic counter" ++ } ++] +\ No newline at end of file diff --git a/target/linux/starfive/patches-6.6/0076-perf-sbi-disable-cpu-hotplug-callback.patch b/target/linux/starfive/patches-6.6/0076-perf-sbi-disable-cpu-hotplug-callback.patch new file mode 100644 index 0000000000..152a60cfda --- /dev/null +++ b/target/linux/starfive/patches-6.6/0076-perf-sbi-disable-cpu-hotplug-callback.patch @@ -0,0 +1,30 @@ +From 30e0cdcf9e05faa65ecde4ed8b70039568fdb660 Mon Sep 17 00:00:00 2001 +From: Minda Chen +Date: Thu, 2 Mar 2023 17:16:01 +0800 +Subject: [PATCH 076/116] perf: sbi: disable cpu hotplug callback. + +register cpu hotplug callback will cause dhrystone +and coremark benchmark reduce the scores. this CPU +hotplug ops will do in sbi cpu/on and off. So disable +this no side effect. + +Signed-off-by: Minda Chen +Signed-off-by: Hal Feng +--- + drivers/perf/riscv_pmu_sbi.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/drivers/perf/riscv_pmu_sbi.c ++++ b/drivers/perf/riscv_pmu_sbi.c +@@ -1114,9 +1114,11 @@ static int pmu_sbi_device_probe(struct p + } + pdev->dev.groups = pmu_sbi_groups; + ++#ifndef CONFIG_ARCH_STARFIVE + ret = cpuhp_state_add_instance(CPUHP_AP_PERF_RISCV_STARTING, &pmu->node); + if (ret) + return ret; ++#endif + + ret = riscv_pm_pmu_register(pmu); + if (ret) diff --git a/target/linux/starfive/patches-6.6/0077-dmaengine-dw-axi-dmac-Drop-unused-print-message.patch b/target/linux/starfive/patches-6.6/0077-dmaengine-dw-axi-dmac-Drop-unused-print-message.patch new file mode 100644 index 0000000000..cdc6e4ec26 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0077-dmaengine-dw-axi-dmac-Drop-unused-print-message.patch @@ -0,0 +1,24 @@ +From fc4b5c7c27e1b56b1f848e50511c4fd081b1b6c5 Mon Sep 17 00:00:00 2001 +From: Walker Chen +Date: Mon, 12 Jun 2023 21:21:45 +0800 +Subject: [PATCH 077/116] dmaengine: dw-axi-dmac: Drop unused print message + +Removed printing information which is not related to StarFive +platform. + +Signed-off-by: Walker Chen +--- + drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c ++++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c +@@ -523,7 +523,7 @@ static void dw_axi_dma_set_hw_channel(st + unsigned long reg_value, val; + + if (!chip->apb_regs) { +- dev_err(chip->dev, "apb_regs not initialized\n"); ++ dev_dbg(chip->dev, "apb_regs not initialized\n"); + return; + } + diff --git a/target/linux/starfive/patches-6.6/0079-ASoC-codecs-Add-AC108-Codec-driver.patch b/target/linux/starfive/patches-6.6/0079-ASoC-codecs-Add-AC108-Codec-driver.patch new file mode 100644 index 0000000000..2a828974ce --- /dev/null +++ b/target/linux/starfive/patches-6.6/0079-ASoC-codecs-Add-AC108-Codec-driver.patch @@ -0,0 +1,4748 @@ +From cd2254c6be9441ebacaa35693ecb5ce116b90622 Mon Sep 17 00:00:00 2001 +From: Xingyu Wu +Date: Fri, 16 Jun 2023 16:27:46 +0800 +Subject: [PATCH 079/116] ASoC: codecs: Add AC108 Codec driver + +Add AC108 Codec driver and AC101 driver for AC10x. + +Signed-off-by: Xingyu Wu +Signed-off-by: Hal Feng +--- + sound/soc/codecs/Kconfig | 5 + + sound/soc/codecs/Makefile | 2 + + sound/soc/codecs/ac101.c | 1716 +++++++++++++++++++++++++++++++++ + sound/soc/codecs/ac101_regs.h | 431 +++++++++ + sound/soc/codecs/ac108.c | 1622 +++++++++++++++++++++++++++++++ + sound/soc/codecs/ac108.h | 749 ++++++++++++++ + sound/soc/codecs/ac10x.h | 152 +++ + 7 files changed, 4677 insertions(+) + create mode 100644 sound/soc/codecs/ac101.c + create mode 100644 sound/soc/codecs/ac101_regs.h + create mode 100644 sound/soc/codecs/ac108.c + create mode 100644 sound/soc/codecs/ac108.h + create mode 100644 sound/soc/codecs/ac10x.h + +--- a/sound/soc/codecs/Kconfig ++++ b/sound/soc/codecs/Kconfig +@@ -16,6 +16,7 @@ config SND_SOC_ALL_CODECS + depends on COMPILE_TEST + imply SND_SOC_88PM860X + imply SND_SOC_AB8500_CODEC ++ imply SND_SOC_AC108 + imply SND_SOC_AC97_CODEC + imply SND_SOC_AD1836 + imply SND_SOC_AD193X_SPI +@@ -397,6 +398,10 @@ config SND_SOC_AB8500_CODEC + tristate + depends on ABX500_CORE + ++config SND_SOC_AC108 ++ tristate "AC108" ++ depends on I2C ++ + config SND_SOC_AC97_CODEC + tristate "Build generic ASoC AC97 CODEC driver" + select SND_AC97_CODEC +--- a/sound/soc/codecs/Makefile ++++ b/sound/soc/codecs/Makefile +@@ -2,6 +2,7 @@ + snd-soc-88pm860x-objs := 88pm860x-codec.o + snd-soc-ab8500-codec-objs := ab8500-codec.o + snd-soc-ac97-objs := ac97.o ++snd-soc-ac108-objs := ac108.o ac101.o + snd-soc-ad1836-objs := ad1836.o + snd-soc-ad193x-objs := ad193x.o + snd-soc-ad193x-spi-objs := ad193x-spi.o +@@ -386,6 +387,7 @@ snd-soc-simple-mux-objs := simple-mux.o + + obj-$(CONFIG_SND_SOC_88PM860X) += snd-soc-88pm860x.o + obj-$(CONFIG_SND_SOC_AB8500_CODEC) += snd-soc-ab8500-codec.o ++obj-$(CONFIG_SND_SOC_AC108) += snd-soc-ac108.o + obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o + obj-$(CONFIG_SND_SOC_AD1836) += snd-soc-ad1836.o + obj-$(CONFIG_SND_SOC_AD193X) += snd-soc-ad193x.o +--- /dev/null ++++ b/sound/soc/codecs/ac101.c +@@ -0,0 +1,1716 @@ ++/* ++ * ac101.c ++ * ++ * (C) Copyright 2017-2018 ++ * Seeed Technology Co., Ltd. ++ * ++ * PeterYang ++ * ++ * (C) Copyright 2014-2017 ++ * Reuuimlla Technology Co., Ltd. ++ * ++ * huangxin ++ * liushaohua ++ * ++ * X-Powers AC101 codec driver ++ * ++ * 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; either version 2 of ++ * the License, or (at your option) any later version. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "ac101_regs.h" ++#include "ac10x.h" ++ ++/* #undef AC101_DEBG ++ * use 'make DEBUG=1' to enable debugging ++ */ ++ ++/* ++ * *** To sync channels *** ++ * ++ * 1. disable clock in codec hw_params() ++ * 2. clear fifo in bcm2835 hw_params() ++ * 3. clear fifo in bcm2385 prepare() ++ * 4. enable RX in bcm2835 trigger() ++ * 5. enable clock in machine trigger() ++ */ ++ ++/*Default initialize configuration*/ ++static bool speaker_double_used = 1; ++static int double_speaker_val = 0x1B; ++static int single_speaker_val = 0x19; ++static int headset_val = 0x3B; ++static int mainmic_val = 0x4; ++static int headsetmic_val = 0x4; ++static bool dmic_used = 0; ++static int adc_digital_val = 0xb0b0; ++static bool drc_used = false; ++ ++#define AC101_RATES (SNDRV_PCM_RATE_8000_96000 & \ ++ ~(SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_64000 | \ ++ SNDRV_PCM_RATE_88200)) ++#define AC101_FORMATS (/*SNDRV_PCM_FMTBIT_S16_LE | \ ++ SNDRV_PCM_FMTBIT_S24_LE |*/ \ ++ SNDRV_PCM_FMTBIT_S32_LE | \ ++ 0) ++ ++static struct ac10x_priv* static_ac10x; ++ ++ ++int ac101_read(struct snd_soc_codec *codec, unsigned reg) { ++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec); ++ int r, v = 0; ++ ++ if ((r = regmap_read(ac10x->regmap101, reg, &v)) < 0) { ++ dev_err(codec->dev, "read reg %02X fail\n", ++ reg); ++ return r; ++ } ++ return v; ++} ++ ++int ac101_write(struct snd_soc_codec *codec, unsigned reg, unsigned val) { ++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec); ++ int v; ++ ++ v = regmap_write(ac10x->regmap101, reg, val); ++ return v; ++} ++ ++int ac101_update_bits(struct snd_soc_codec *codec, unsigned reg, ++ unsigned mask, unsigned value ++) { ++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec); ++ int v; ++ ++ v = regmap_update_bits(ac10x->regmap101, reg, mask, value); ++ return v; ++} ++ ++ ++ ++#ifdef CONFIG_AC101_SWITCH_DETECT ++/******************************************************************************/ ++/********************************switch****************************************/ ++/******************************************************************************/ ++#define KEY_HEADSETHOOK 226 /* key define */ ++#define HEADSET_FILTER_CNT (10) ++ ++/* ++ * switch_hw_config:config the 53 codec register ++ */ ++static void switch_hw_config(struct snd_soc_codec *codec) ++{ ++ int r; ++ ++ AC101_DBG(); ++ ++ /*HMIC/MMIC BIAS voltage level select:2.5v*/ ++ ac101_update_bits(codec, OMIXER_BST1_CTRL, (0xf<state:%d\n", ac10x->state); ++ ++ input_report_switch(ac10x->inpdev, SW_HEADPHONE_INSERT, ac10x->state); ++ input_sync(ac10x->inpdev); ++ return; ++} ++ ++/* ++ * work_cb_clear_irq: clear audiocodec pending and Record the interrupt. ++ */ ++static void work_cb_clear_irq(struct work_struct *work) ++{ ++ int reg_val = 0; ++ struct ac10x_priv *ac10x = container_of(work, struct ac10x_priv, work_clear_irq); ++ struct snd_soc_codec *codec = ac10x->codec; ++ ++ ac10x->irq_cntr++; ++ ++ reg_val = ac101_read(codec, HMIC_STS); ++ if (BIT(HMIC_PULLOUT_PEND) & reg_val) { ++ ac10x->pullout_cntr++; ++ AC101_DBG("ac10x->pullout_cntr: %d\n", ac10x->pullout_cntr); ++ } ++ ++ reg_val |= HMIC_PEND_ALL; ++ ac101_write(codec, HMIC_STS, reg_val); ++ ++ reg_val = ac101_read(codec, HMIC_STS); ++ if ((reg_val & HMIC_PEND_ALL) != 0){ ++ reg_val |= HMIC_PEND_ALL; ++ ac101_write(codec, HMIC_STS, reg_val); ++ } ++ ++ if (cancel_work_sync(&ac10x->work_switch) != 0) { ++ ac10x->irq_cntr--; ++ } ++ ++ if (0 == schedule_work(&ac10x->work_switch)) { ++ ac10x->irq_cntr--; ++ AC101_DBG("[work_cb_clear_irq] add work struct failed!\n"); ++ } ++} ++ ++enum { ++ HBIAS_LEVEL_1 = 0x02, ++ HBIAS_LEVEL_2 = 0x0B, ++ HBIAS_LEVEL_3 = 0x13, ++ HBIAS_LEVEL_4 = 0x17, ++ HBIAS_LEVEL_5 = 0x19, ++}; ++ ++static int __ac101_get_hmic_data(struct snd_soc_codec *codec) { ++ #ifdef AC101_DEBG ++ static long counter; ++ #endif ++ int r, d; ++ ++ d = GET_HMIC_DATA(ac101_read(codec, HMIC_STS)); ++ ++ r = 0x1 << HMIC_DATA_PEND; ++ ac101_write(codec, HMIC_STS, r); ++ ++ /* prevent i2c accessing too frequently */ ++ usleep_range(1500, 3000); ++ ++ AC101_DBG("HMIC_DATA(%3ld): %02X\n", counter++, d); ++ return d; ++} ++ ++/* ++ * work_cb_earphone_switch: judge the status of the headphone ++ */ ++static void work_cb_earphone_switch(struct work_struct *work) ++{ ++ struct ac10x_priv *ac10x = container_of(work, struct ac10x_priv, work_switch); ++ struct snd_soc_codec *codec = ac10x->codec; ++ ++ static int hook_flag1 = 0, hook_flag2 = 0; ++ static int KEY_VOLUME_FLAG = 0; ++ ++ unsigned filter_buf = 0; ++ int filt_index = 0; ++ int t = 0; ++ ++ ac10x->irq_cntr--; ++ ++ /* read HMIC_DATA */ ++ t = __ac101_get_hmic_data(codec); ++ ++ if ((t >= HBIAS_LEVEL_2) && (ac10x->mode == FOUR_HEADPHONE_PLUGIN)) { ++ t = __ac101_get_hmic_data(codec); ++ ++ if (t >= HBIAS_LEVEL_5){ ++ msleep(150); ++ t = __ac101_get_hmic_data(codec); ++ if (((t < HBIAS_LEVEL_2 && t >= HBIAS_LEVEL_1 - 1) || t >= HBIAS_LEVEL_5) ++ && (ac10x->pullout_cntr == 0)) { ++ input_report_key(ac10x->inpdev, KEY_HEADSETHOOK, 1); ++ input_sync(ac10x->inpdev); ++ ++ AC101_DBG("KEY_HEADSETHOOK1\n"); ++ ++ if (hook_flag1 != hook_flag2) ++ hook_flag1 = hook_flag2 = 0; ++ hook_flag1++; ++ } ++ if (ac10x->pullout_cntr) ++ ac10x->pullout_cntr--; ++ } else if (t >= HBIAS_LEVEL_4) { ++ msleep(80); ++ t = __ac101_get_hmic_data(codec); ++ if (t < HBIAS_LEVEL_5 && t >= HBIAS_LEVEL_4 && (ac10x->pullout_cntr == 0)) { ++ KEY_VOLUME_FLAG = 1; ++ input_report_key(ac10x->inpdev, KEY_VOLUMEUP, 1); ++ input_sync(ac10x->inpdev); ++ input_report_key(ac10x->inpdev, KEY_VOLUMEUP, 0); ++ input_sync(ac10x->inpdev); ++ ++ AC101_DBG("HMIC_DATA: %d KEY_VOLUMEUP\n", t); ++ } ++ if (ac10x->pullout_cntr) ++ ac10x->pullout_cntr--; ++ } else if (t >= HBIAS_LEVEL_3){ ++ msleep(80); ++ t = __ac101_get_hmic_data(codec); ++ if (t < HBIAS_LEVEL_4 && t >= HBIAS_LEVEL_3 && (ac10x->pullout_cntr == 0)) { ++ KEY_VOLUME_FLAG = 1; ++ input_report_key(ac10x->inpdev, KEY_VOLUMEDOWN, 1); ++ input_sync(ac10x->inpdev); ++ input_report_key(ac10x->inpdev, KEY_VOLUMEDOWN, 0); ++ input_sync(ac10x->inpdev); ++ AC101_DBG("KEY_VOLUMEDOWN\n"); ++ } ++ if (ac10x->pullout_cntr) ++ ac10x->pullout_cntr--; ++ } ++ } else if ((t < HBIAS_LEVEL_2 && t >= HBIAS_LEVEL_1) && ++ (ac10x->mode == FOUR_HEADPHONE_PLUGIN)) { ++ t = __ac101_get_hmic_data(codec); ++ if (t < HBIAS_LEVEL_2 && t >= HBIAS_LEVEL_1) { ++ if (KEY_VOLUME_FLAG) { ++ KEY_VOLUME_FLAG = 0; ++ } ++ if (hook_flag1 == (++hook_flag2)) { ++ hook_flag1 = hook_flag2 = 0; ++ input_report_key(ac10x->inpdev, KEY_HEADSETHOOK, 0); ++ input_sync(ac10x->inpdev); ++ ++ AC101_DBG("KEY_HEADSETHOOK0\n"); ++ } ++ } ++ } else { ++ while (ac10x->irq_cntr == 0 && ac10x->irq != 0) { ++ msleep(20); ++ ++ t = __ac101_get_hmic_data(codec); ++ ++ if (filt_index <= HEADSET_FILTER_CNT) { ++ if (filt_index++ == 0) { ++ filter_buf = t; ++ } else if (filter_buf != t) { ++ filt_index = 0; ++ } ++ continue; ++ } ++ ++ filt_index = 0; ++ if (filter_buf >= HBIAS_LEVEL_2) { ++ ac10x->mode = THREE_HEADPHONE_PLUGIN; ++ ac10x->state = 2; ++ } else if (filter_buf >= HBIAS_LEVEL_1 - 1) { ++ ac10x->mode = FOUR_HEADPHONE_PLUGIN; ++ ac10x->state = 1; ++ } else { ++ ac10x->mode = HEADPHONE_IDLE; ++ ac10x->state = 0; ++ } ++ switch_status_update(ac10x); ++ ac10x->pullout_cntr = 0; ++ break; ++ } ++ } ++} ++ ++/* ++ * audio_hmic_irq: the interrupt handlers ++ */ ++static irqreturn_t audio_hmic_irq(int irq, void *para) ++{ ++ struct ac10x_priv *ac10x = (struct ac10x_priv *)para; ++ if (ac10x == NULL) { ++ return -EINVAL; ++ } ++ ++ if (0 == schedule_work(&ac10x->work_clear_irq)){ ++ AC101_DBG("[audio_hmic_irq] work already in queue_codec_irq, adding failed!\n"); ++ } ++ return IRQ_HANDLED; ++} ++ ++static int ac101_switch_probe(struct ac10x_priv *ac10x) { ++ struct i2c_client *i2c = ac10x->i2c101; ++ long ret; ++ ++ ac10x->gpiod_irq = devm_gpiod_get_optional(&i2c->dev, "switch-irq", GPIOD_IN); ++ if (IS_ERR(ac10x->gpiod_irq)) { ++ ac10x->gpiod_irq = NULL; ++ dev_err(&i2c->dev, "failed get switch-irq in device tree\n"); ++ goto _err_irq; ++ } ++ ++ gpiod_direction_input(ac10x->gpiod_irq); ++ ++ ac10x->irq = gpiod_to_irq(ac10x->gpiod_irq); ++ if (IS_ERR_VALUE(ac10x->irq)) { ++ pr_info("[ac101] map gpio to irq failed, errno = %ld\n", ac10x->irq); ++ ac10x->irq = 0; ++ goto _err_irq; ++ } ++ ++ /* request irq, set irq type to falling edge trigger */ ++ ret = devm_request_irq(ac10x->codec->dev, ac10x->irq, audio_hmic_irq, ++ IRQF_TRIGGER_FALLING, "SWTICH_EINT", ac10x); ++ if (IS_ERR_VALUE(ret)) { ++ pr_info("[ac101] request virq %ld failed, errno = %ld\n", ac10x->irq, ret); ++ goto _err_irq; ++ } ++ ++ ac10x->mode = HEADPHONE_IDLE; ++ ac10x->state = -1; ++ ++ /*use for judge the state of switch*/ ++ INIT_WORK(&ac10x->work_switch, work_cb_earphone_switch); ++ INIT_WORK(&ac10x->work_clear_irq, work_cb_clear_irq); ++ ++ /********************create input device************************/ ++ ac10x->inpdev = devm_input_allocate_device(ac10x->codec->dev); ++ if (!ac10x->inpdev) { ++ AC101_DBG("input_allocate_device: not enough memory for input device\n"); ++ ret = -ENOMEM; ++ goto _err_input_allocate_device; ++ } ++ ++ ac10x->inpdev->name = "seed-voicecard-headset"; ++ ac10x->inpdev->phys = dev_name(ac10x->codec->dev); ++ ac10x->inpdev->id.bustype = BUS_I2C; ++ ac10x->inpdev->dev.parent = ac10x->codec->dev; ++ input_set_drvdata(ac10x->inpdev, ac10x->codec); ++ ++ ac10x->inpdev->evbit[0] = BIT_MASK(EV_KEY) | BIT(EV_SW); ++ ++ set_bit(KEY_HEADSETHOOK, ac10x->inpdev->keybit); ++ set_bit(KEY_VOLUMEUP, ac10x->inpdev->keybit); ++ set_bit(KEY_VOLUMEDOWN, ac10x->inpdev->keybit); ++ input_set_capability(ac10x->inpdev, EV_SW, SW_HEADPHONE_INSERT); ++ ++ ret = input_register_device(ac10x->inpdev); ++ if (ret) { ++ AC101_DBG("input_register_device: input_register_device failed\n"); ++ goto _err_input_register_device; ++ } ++ ++ /* the first headset state checking */ ++ switch_hw_config(ac10x->codec); ++ ac10x->irq_cntr = 1; ++ schedule_work(&ac10x->work_switch); ++ ++ return 0; ++ ++_err_input_register_device: ++_err_input_allocate_device: ++ ++ if (ac10x->irq) { ++ devm_free_irq(&i2c->dev, ac10x->irq, ac10x); ++ ac10x->irq = 0; ++ } ++_err_irq: ++ return ret; ++} ++/******************************************************************************/ ++/********************************switch****************************************/ ++/******************************************************************************/ ++#endif ++ ++ ++ ++void drc_config(struct snd_soc_codec *codec) ++{ ++ int reg_val; ++ reg_val = ac101_read(codec, 0xa3); ++ reg_val &= ~(0x7ff<<0); ++ reg_val |= 1<<0; ++ ac101_write(codec, 0xa3, reg_val); ++ ac101_write(codec, 0xa4, 0x2baf); ++ ++ reg_val = ac101_read(codec, 0xa5); ++ reg_val &= ~(0x7ff<<0); ++ reg_val |= 1<<0; ++ ac101_write(codec, 0xa5, reg_val); ++ ac101_write(codec, 0xa6, 0x2baf); ++ ++ reg_val = ac101_read(codec, 0xa7); ++ reg_val &= ~(0x7ff<<0); ++ ac101_write(codec, 0xa7, reg_val); ++ ac101_write(codec, 0xa8, 0x44a); ++ ++ reg_val = ac101_read(codec, 0xa9); ++ reg_val &= ~(0x7ff<<0); ++ ac101_write(codec, 0xa9, reg_val); ++ ac101_write(codec, 0xaa, 0x1e06); ++ ++ reg_val = ac101_read(codec, 0xab); ++ reg_val &= ~(0x7ff<<0); ++ reg_val |= (0x352<<0); ++ ac101_write(codec, 0xab, reg_val); ++ ac101_write(codec, 0xac, 0x6910); ++ ++ reg_val = ac101_read(codec, 0xad); ++ reg_val &= ~(0x7ff<<0); ++ reg_val |= (0x77a<<0); ++ ac101_write(codec, 0xad, reg_val); ++ ac101_write(codec, 0xae, 0xaaaa); ++ ++ reg_val = ac101_read(codec, 0xaf); ++ reg_val &= ~(0x7ff<<0); ++ reg_val |= (0x2de<<0); ++ ac101_write(codec, 0xaf, reg_val); ++ ac101_write(codec, 0xb0, 0xc982); ++ ++ ac101_write(codec, 0x16, 0x9f9f); ++ ++} ++ ++void drc_enable(struct snd_soc_codec *codec,bool on) ++{ ++ int reg_val; ++ if (on) { ++ ac101_write(codec, 0xb5, 0xA080); ++ reg_val = ac101_read(codec, MOD_CLK_ENA); ++ reg_val |= (0x1<<6); ++ ac101_write(codec, MOD_CLK_ENA, reg_val); ++ reg_val = ac101_read(codec, MOD_RST_CTRL); ++ reg_val |= (0x1<<6); ++ ac101_write(codec, MOD_RST_CTRL, reg_val); ++ ++ reg_val = ac101_read(codec, 0xa0); ++ reg_val |= (0x7<<0); ++ ac101_write(codec, 0xa0, reg_val); ++ } else { ++ ac101_write(codec, 0xb5, 0x0); ++ reg_val = ac101_read(codec, MOD_CLK_ENA); ++ reg_val &= ~(0x1<<6); ++ ac101_write(codec, MOD_CLK_ENA, reg_val); ++ reg_val = ac101_read(codec, MOD_RST_CTRL); ++ reg_val &= ~(0x1<<6); ++ ac101_write(codec, MOD_RST_CTRL, reg_val); ++ ++ reg_val = ac101_read(codec, 0xa0); ++ reg_val &= ~(0x7<<0); ++ ac101_write(codec, 0xa0, reg_val); ++ } ++} ++ ++void set_configuration(struct snd_soc_codec *codec) ++{ ++ if (speaker_double_used) { ++ ac101_update_bits(codec, SPKOUT_CTRL, (0x1f<dac_mutex); ++ switch (event) { ++ case SND_SOC_DAPM_PRE_PMU: ++ AC101_DBG(); ++ if (ac10x->dac_enable == 0){ ++ /*enable dac module clk*/ ++ ac101_update_bits(codec, MOD_CLK_ENA, (0x1<dac_enable++; ++ break; ++ case SND_SOC_DAPM_POST_PMD: ++ if (ac10x->dac_enable != 0){ ++ ac10x->dac_enable = 0; ++ ++ ac101_update_bits(codec, DAC_DIG_CTRL, (0x1<dac_mutex); ++ return 0; ++} ++ ++static int ac101_headphone_event(struct snd_soc_codec* codec, int event) { ++ switch (event) { ++ case SND_SOC_DAPM_POST_PMU: ++ /*open*/ ++ AC101_DBG("post:open\n"); ++ ac101_update_bits(codec, OMIXER_DACA_CTRL, (0xf<codec, SYSCLK_CTRL); ++ return (reg_val & (0x1<aif1_clken == 0){ ++ ret = ac101_update_bits(codec, SYSCLK_CTRL, (0x1<aif1_clken++; ++ } ++ } ++ break; ++ case SND_SOC_DAPM_POST_PMD: ++ if (ac10x->aif1_clken != 0) { ++ /* disable aif1clk & sysclk */ ++ ret = ac101_update_bits(codec, SYSCLK_CTRL, (0x1<aif1_clken = 0; ++ } ++ break; ++ } ++ } ++ ++ AC101_DBG("event=%d pre_up/%d post_down/%d\n", event, SND_SOC_DAPM_PRE_PMU, ++ SND_SOC_DAPM_POST_PMD); ++ ++ return ret; ++} ++ ++/** ++ * snd_ac101_get_volsw - single mixer get callback ++ * @kcontrol: mixer control ++ * @ucontrol: control element information ++ * ++ * Callback to get the value of a single mixer control, or a double mixer ++ * control that spans 2 registers. ++ * ++ * Returns 0 for success. ++ */ ++static int snd_ac101_get_volsw(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol ++){ ++ struct soc_mixer_control *mc = ++ (struct soc_mixer_control *)kcontrol->private_value; ++ unsigned int val, mask = (1 << fls(mc->max)) - 1; ++ unsigned int invert = mc->invert; ++ int ret; ++ ++ if ((ret = ac101_read(static_ac10x->codec, mc->reg)) < 0) ++ return ret; ++ ++ val = (ret >> mc->shift) & mask; ++ ucontrol->value.integer.value[0] = val - mc->min; ++ if (invert) { ++ ucontrol->value.integer.value[0] = ++ mc->max - ucontrol->value.integer.value[0]; ++ } ++ ++ if (snd_soc_volsw_is_stereo(mc)) { ++ val = (ret >> mc->rshift) & mask; ++ ucontrol->value.integer.value[1] = val - mc->min; ++ if (invert) { ++ ucontrol->value.integer.value[1] = ++ mc->max - ucontrol->value.integer.value[1]; ++ } ++ } ++ return 0; ++} ++ ++/** ++ * snd_ac101_put_volsw - single mixer put callback ++ * @kcontrol: mixer control ++ * @ucontrol: control element information ++ * ++ * Callback to set the value of a single mixer control, or a double mixer ++ * control that spans 2 registers. ++ * ++ * Returns 0 for success. ++ */ ++static int snd_ac101_put_volsw(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol ++){ ++ struct soc_mixer_control *mc = ++ (struct soc_mixer_control *)kcontrol->private_value; ++ unsigned int sign_bit = mc->sign_bit; ++ unsigned int val, mask = (1 << fls(mc->max)) - 1; ++ unsigned int invert = mc->invert; ++ int ret; ++ ++ if (sign_bit) ++ mask = BIT(sign_bit + 1) - 1; ++ ++ val = ((ucontrol->value.integer.value[0] + mc->min) & mask); ++ if (invert) { ++ val = mc->max - val; ++ } ++ ++ ret = ac101_update_bits(static_ac10x->codec, mc->reg, mask << mc->shift, val << mc->shift); ++ ++ if (! snd_soc_volsw_is_stereo(mc)) { ++ return ret; ++ } ++ val = ((ucontrol->value.integer.value[1] + mc->min) & mask); ++ if (invert) { ++ val = mc->max - val; ++ } ++ ++ ret = ac101_update_bits(static_ac10x->codec, mc->reg, mask << mc->rshift, ++ val << mc->rshift); ++ return ret; ++} ++ ++ ++static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -11925, 75, 0); ++static const DECLARE_TLV_DB_SCALE(dac_mix_vol_tlv, -600, 600, 0); ++static const DECLARE_TLV_DB_SCALE(dig_vol_tlv, -7308, 116, 0); ++static const DECLARE_TLV_DB_SCALE(speaker_vol_tlv, -4800, 150, 0); ++static const DECLARE_TLV_DB_SCALE(headphone_vol_tlv, -6300, 100, 0); ++ ++static struct snd_kcontrol_new ac101_controls[] = { ++ /*DAC*/ ++ SOC_DOUBLE_TLV("DAC volume", DAC_VOL_CTRL, DAC_VOL_L, DAC_VOL_R, 0xff, 0, dac_vol_tlv), ++ SOC_DOUBLE_TLV("DAC mixer gain", DAC_MXR_GAIN, DACL_MXR_GAIN, DACR_MXR_GAIN, ++ 0xf, 0, dac_mix_vol_tlv), ++ SOC_SINGLE_TLV("digital volume", DAC_DBG_CTRL, DVC, 0x3f, 1, dig_vol_tlv), ++ SOC_SINGLE_TLV("speaker volume", SPKOUT_CTRL, SPK_VOL, 0x1f, 0, speaker_vol_tlv), ++ SOC_SINGLE_TLV("headphone volume", HPOUT_CTRL, HP_VOL, 0x3f, 0, headphone_vol_tlv), ++}; ++ ++/* PLL divisors */ ++struct pll_div { ++ unsigned int pll_in; ++ unsigned int pll_out; ++ int m; ++ int n_i; ++ int n_f; ++}; ++ ++struct aif1_fs { ++ unsigned samp_rate; ++ int bclk_div; ++ int srbit; ++ #define _SERIES_24_576K 0 ++ #define _SERIES_22_579K 1 ++ int series; ++}; ++ ++struct kv_map { ++ int val; ++ int bit; ++}; ++ ++/* ++ * Note : pll code from original tdm/i2s driver. ++ * freq_out = freq_in * N/(M*(2k+1)) , k=1,N=N_i+N_f,N_f=factor*0.2; ++ * N_i[0,1023], N_f_factor[0,7], m[1,64]=REG_VAL[1-63,0] ++ */ ++static const struct pll_div codec_pll_div[] = { ++ {128000, _FREQ_22_579K, 1, 529, 1}, ++ {192000, _FREQ_22_579K, 1, 352, 4}, ++ {256000, _FREQ_22_579K, 1, 264, 3}, ++ {384000, _FREQ_22_579K, 1, 176, 2}, /*((176+2*0.2)*6000000)/(38*(2*1+1))*/ ++ {1411200, _FREQ_22_579K, 1, 48, 0}, ++ {2822400, _FREQ_22_579K, 1, 24, 0}, /* accurate, 11025 * 256 */ ++ {5644800, _FREQ_22_579K, 1, 12, 0}, /* accurate, 22050 * 256 */ ++ {6000000, _FREQ_22_579K, 38, 429, 0}, /*((429+0*0.2)*6000000)/(38*(2*1+1))*/ ++ {11289600, _FREQ_22_579K, 1, 6, 0}, /* accurate, 44100 * 256 */ ++ {13000000, _FREQ_22_579K, 19, 99, 0}, ++ {19200000, _FREQ_22_579K, 25, 88, 1}, ++ {24000000, _FREQ_22_579K, 63, 177, 4}, /* 22577778 Hz */ ++ ++ {128000, _FREQ_24_576K, 1, 576, 0}, ++ {192000, _FREQ_24_576K, 1, 384, 0}, ++ {256000, _FREQ_24_576K, 1, 288, 0}, ++ {384000, _FREQ_24_576K, 1, 192, 0}, ++ {2048000, _FREQ_24_576K, 1, 36, 0}, /* accurate, 8000 * 256 */ ++ {3072000, _FREQ_24_576K, 1, 24, 0}, /* accurate, 12000 * 256 */ ++ {4096000, _FREQ_24_576K, 1, 18, 0}, /* accurate, 16000 * 256 */ ++ {6000000, _FREQ_24_576K, 25, 307, 1}, ++ {6144000, _FREQ_24_576K, 4, 48, 0}, /* accurate, 24000 * 256 */ ++ {12288000, _FREQ_24_576K, 8, 48, 0}, /* accurate, 48000 * 256 */ ++ {13000000, _FREQ_24_576K, 42, 238, 1}, ++ {19200000, _FREQ_24_576K, 25, 96, 0}, ++ {24000000, _FREQ_24_576K, 25, 76, 4}, /* accurate */ ++ ++ {_FREQ_22_579K, _FREQ_22_579K, 8, 24, 0}, /* accurate, 88200 * 256 */ ++ {_FREQ_24_576K, _FREQ_24_576K, 8, 24, 0}, /* accurate, 96000 * 256 */ ++}; ++ ++static const struct aif1_fs codec_aif1_fs[] = { ++ {8000, 12, 0}, ++ {11025, 8, 1, _SERIES_22_579K}, ++ {12000, 8, 2}, ++ {16000, 6, 3}, ++ {22050, 4, 4, _SERIES_22_579K}, ++ {24000, 4, 5}, ++ /* {32000, 3, 6}, dividing by 3 is not support */ ++ {44100, 2, 7, _SERIES_22_579K}, ++ {48000, 2, 8}, ++ {96000, 1, 9}, ++}; ++ ++static const struct kv_map codec_aif1_lrck[] = { ++ {16, 0}, ++ {32, 1}, ++ {64, 2}, ++ {128, 3}, ++ {256, 4}, ++}; ++ ++static const struct kv_map codec_aif1_wsize[] = { ++ {8, 0}, ++ {16, 1}, ++ {20, 2}, ++ {24, 3}, ++ {32, 3}, ++}; ++ ++static const unsigned ac101_bclkdivs[] = { ++ 1, 2, 4, 6, ++ 8, 12, 16, 24, ++ 32, 48, 64, 96, ++ 128, 192, 0, 0, ++}; ++ ++static int ac101_aif_play(struct ac10x_priv* ac10x) { ++ struct snd_soc_codec * codec = ac10x->codec; ++ ++ late_enable_dac(codec, SND_SOC_DAPM_PRE_PMU); ++ ac101_headphone_event(codec, SND_SOC_DAPM_POST_PMU); ++ if (drc_used) { ++ drc_enable(codec, 1); ++ } ++ ++ /* Enable Left & Right Speaker */ ++ ac101_update_bits(codec, SPKOUT_CTRL, (0x1 << LSPK_EN) | (0x1 << RSPK_EN), ++ (0x1 << LSPK_EN) | (0x1 << RSPK_EN)); ++ if (ac10x->gpiod_spk_amp_gate) { ++ gpiod_set_value(ac10x->gpiod_spk_amp_gate, 1); ++ } ++ return 0; ++} ++ ++static void ac10x_work_aif_play(struct work_struct *work) { ++ struct ac10x_priv *ac10x = container_of(work, struct ac10x_priv, dlywork.work); ++ ++ ac101_aif_play(ac10x); ++ return; ++} ++ ++int ac101_aif_mute(struct snd_soc_dai *codec_dai, int mute) ++{ ++ struct snd_soc_codec *codec = codec_dai->codec; ++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec); ++ ++ AC101_DBG("mute=%d\n", mute); ++ ++ ac101_write(codec, DAC_VOL_CTRL, mute? 0: 0xA0A0); ++ ++ if (!mute) { ++ #if _MASTER_MULTI_CODEC != _MASTER_AC101 ++ /* enable global clock */ ++ ac10x->aif1_clken = 0; ++ ac101_aif1clk(codec, SND_SOC_DAPM_PRE_PMU, 0); ++ ac101_aif_play(ac10x); ++ #else ++ schedule_delayed_work(&ac10x->dlywork, msecs_to_jiffies(50)); ++ #endif ++ } else { ++ #if _MASTER_MULTI_CODEC == _MASTER_AC101 ++ cancel_delayed_work_sync(&ac10x->dlywork); ++ #endif ++ ++ if (ac10x->gpiod_spk_amp_gate) { ++ gpiod_set_value(ac10x->gpiod_spk_amp_gate, 0); ++ } ++ /* Disable Left & Right Speaker */ ++ ac101_update_bits(codec, SPKOUT_CTRL, (0x1 << LSPK_EN) | (0x1 << RSPK_EN), ++ (0x0 << LSPK_EN) | (0x0 << RSPK_EN)); ++ if (drc_used) { ++ drc_enable(codec, 0); ++ } ++ ac101_headphone_event(codec, SND_SOC_DAPM_PRE_PMD); ++ late_enable_dac(codec, SND_SOC_DAPM_POST_PMD); ++ ++ #if _MASTER_MULTI_CODEC != _MASTER_AC101 ++ ac10x->aif1_clken = 1; ++ ac101_aif1clk(codec, SND_SOC_DAPM_POST_PMD, 0); ++ #endif ++ } ++ return 0; ++} ++ ++void ac101_aif_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai) ++{ ++ struct snd_soc_codec *codec = codec_dai->codec; ++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec); ++ ++ AC101_DBG("stream = %s, play: %d, capt: %d, active: %d\n", ++ snd_pcm_stream_str(substream), ++ codec_dai->stream[SNDRV_PCM_STREAM_PLAYBACK].active, ++ codec_dai->stream[SNDRV_PCM_STREAM_CAPTURE].active, ++ snd_soc_dai_active(codec_dai)); ++ ++ if (!snd_soc_dai_active(codec_dai)) { ++ ac10x->aif1_clken = 1; ++ ac101_aif1clk(codec, SND_SOC_DAPM_POST_PMD, 0); ++ } else { ++ ac101_aif1clk(codec, SND_SOC_DAPM_PRE_PMU, 0); ++ } ++} ++ ++static int ac101_set_pll(struct snd_soc_dai *codec_dai, int pll_id, int source, ++ unsigned int freq_in, unsigned int freq_out) ++{ ++ struct snd_soc_codec *codec = codec_dai->codec; ++ int i, m, n_i, n_f; ++ ++ AC101_DBG("pll_id:%d\n", pll_id); ++ ++ /* clear volatile reserved bits*/ ++ ac101_update_bits(codec, SYSCLK_CTRL, 0xFF & ~(0x1 << SYSCLK_ENA), 0x0); ++ ++ /* select aif1 clk srouce from mclk1 */ ++ ac101_update_bits(codec, SYSCLK_CTRL, (0x3< _FREQ_24_576K)) { ++ return -EINVAL; ++ } else if ((freq_in == _FREQ_24_576K) || (freq_in == _FREQ_22_579K)) { ++ if (pll_id == AC101_MCLK1) { ++ /*select aif1 clk source from mclk1*/ ++ ac101_update_bits(codec, SYSCLK_CTRL, (0x3<codec; ++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec); ++ int reg_val, freq_out; ++ unsigned channels; ++ ++ AC101_DBG("+++\n"); ++ ++ if (_MASTER_MULTI_CODEC == _MASTER_AC101 && ac101_sysclk_started()) { ++ /* not configure hw_param twice if stream is playback, tell the caller it's started */ ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { ++ return 1; ++ } ++ } ++ ++ /* get channels count & slot size */ ++ channels = params_channels(params); ++ ++ switch (params_format(params)) { ++ case SNDRV_PCM_FORMAT_S24_LE: ++ case SNDRV_PCM_FORMAT_S32_LE: ++ aif1_slot_size = 32; ++ break; ++ case SNDRV_PCM_FORMAT_S16_LE: ++ default: ++ aif1_slot_size = 16; ++ break; ++ } ++ ++ /* set LRCK/BCLK ratio */ ++ aif1_lrck_div = aif1_slot_size * channels; ++ for (i = 0; i < ARRAY_SIZE(codec_aif1_lrck); i++) { ++ if (codec_aif1_lrck[i].val == aif1_lrck_div) { ++ break; ++ } ++ } ++ ac101_update_bits(codec, AIF_CLK_CTRL, (0x7 << AIF1_LRCK_DIV), ++ codec_aif1_lrck[i].bit << AIF1_LRCK_DIV); ++ ++ /* set PLL output freq */ ++ freq_out = _FREQ_24_576K; ++ for (i = 0; i < ARRAY_SIZE(codec_aif1_fs); i++) { ++ if (codec_aif1_fs[i].samp_rate == params_rate(params)) { ++ if (codec_dai->stream[SNDRV_PCM_STREAM_CAPTURE].active && dmic_used && ++ codec_aif1_fs[i].samp_rate == 44100) { ++ ac101_update_bits(codec, AIF_SR_CTRL, (0xf< 2) reg_val = 2; ++ ac101_update_bits(codec, AIF1_ADCDAT_CTRL, 0x3 << AIF1_SLOT_SIZ, reg_val << AIF1_SLOT_SIZ); ++ ++ /* setting pll if it's master mode */ ++ reg_val = ac101_read(codec, AIF_CLK_CTRL); ++ if ((reg_val & (0x1 << AIF1_MSTR_MOD)) == 0) { ++ unsigned bclkdiv; ++ ++ ac101_set_pll(codec_dai, AC101_MCLK1, 0, ac10x->sysclk, freq_out); ++ ++ bclkdiv = freq_out / (aif1_lrck_div * params_rate(params)); ++ for (i = 0; i < ARRAY_SIZE(ac101_bclkdivs) - 1; i++) { ++ if (ac101_bclkdivs[i] >= bclkdiv) { ++ break; ++ } ++ } ++ ac101_update_bits(codec, AIF_CLK_CTRL, (0xf<codec; ++ ++ AC101_DBG(); ++ ++ /* ++ * master or slave selection ++ * 0 = Master mode ++ * 1 = Slave mode ++ */ ++ reg_val = ac101_read(codec, AIF_CLK_CTRL); ++ reg_val &= ~(0x1<codec; ++ ++ AC101_DBG("\n\n\n"); ++ ++ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { ++ } ++ return 0; ++} ++ ++int ac101_trigger(struct snd_pcm_substream *substream, int cmd, ++ struct snd_soc_dai *dai) ++{ ++ struct snd_soc_codec *codec = dai->codec; ++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec); ++ int ret = 0; ++ ++ AC101_DBG("stream=%s cmd=%d\n", ++ snd_pcm_stream_str(substream), ++ cmd); ++ ++ switch (cmd) { ++ case SNDRV_PCM_TRIGGER_START: ++ case SNDRV_PCM_TRIGGER_RESUME: ++ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: ++ #if _MASTER_MULTI_CODEC == _MASTER_AC101 ++ if (ac10x->aif1_clken == 0){ ++ /* ++ * enable aif1clk, it' here due to reduce time between 'AC108 Sysclk Enable' and 'AC101 Sysclk Enable' ++ * Or else the two AC108 chips lost the sync. ++ */ ++ ret = 0; ++ ret = ret || ac101_update_bits(codec, MOD_CLK_ENA, ++ (0x1<codec; ++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec); ++ ++ AC101_DBG("id=%d freq=%d, dir=%d\n", ++ clk_id, freq, dir); ++ ++ ac10x->sysclk = freq; ++ ++ return 0; ++} ++ ++static const struct snd_soc_dai_ops ac101_aif1_dai_ops = { ++ //.startup = ac101_audio_startup, ++ //.shutdown = ac101_aif_shutdown, ++ //.set_sysclk = ac101_set_dai_sysclk, ++ //.set_pll = ac101_set_pll, ++ //.set_fmt = ac101_set_dai_fmt, ++ //.hw_params = ac101_hw_params, ++ //.trigger = ac101_trigger, ++ //.digital_mute = ac101_aif_mute, ++}; ++ ++static struct snd_soc_dai_driver ac101_dai[] = { ++ { ++ .name = "ac10x-aif1", ++ .id = AIF1_CLK, ++ .playback = { ++ .stream_name = "Playback", ++ .channels_min = 1, ++ .channels_max = 8, ++ .rates = AC101_RATES, ++ .formats = AC101_FORMATS, ++ }, ++ #if 0 ++ .capture = { ++ .stream_name = "Capture", ++ .channels_min = 1, ++ .channels_max = 8, ++ .rates = AC101_RATES, ++ .formats = AC101_FORMATS, ++ }, ++ #endif ++ .ops = &ac101_aif1_dai_ops, ++ } ++}; ++#endif ++ ++static void codec_resume_work(struct work_struct *work) ++{ ++ struct ac10x_priv *ac10x = container_of(work, struct ac10x_priv, codec_resume); ++ struct snd_soc_codec *codec = ac10x->codec; ++ ++ AC101_DBG("+++\n"); ++ ++ set_configuration(codec); ++ if (drc_used) { ++ drc_config(codec); ++ } ++ /*enable this bit to prevent leakage from ldoin*/ ++ ac101_update_bits(codec, ADDA_TUNE3, (0x1<bias_level = level; ++ return 0; ++} ++ ++int ac101_codec_probe(struct snd_soc_codec *codec) ++{ ++ int ret = 0; ++ struct ac10x_priv *ac10x; ++ ++ ac10x = dev_get_drvdata(codec->dev); ++ if (ac10x == NULL) { ++ AC101_DBG("not set client data!\n"); ++ return -ENOMEM; ++ } ++ ac10x->codec = codec; ++ ++ INIT_DELAYED_WORK(&ac10x->dlywork, ac10x_work_aif_play); ++ INIT_WORK(&ac10x->codec_resume, codec_resume_work); ++ ac10x->dac_enable = 0; ++ ac10x->aif1_clken = 0; ++ mutex_init(&ac10x->dac_mutex); ++ ++ set_configuration(ac10x->codec); ++ ++ /*enable this bit to prevent leakage from ldoin*/ ++ ac101_update_bits(codec, ADDA_TUNE3, (0x1<get = snd_ac101_get_volsw; ++ skn->put = snd_ac101_put_volsw; ++ } ++ ret = snd_soc_add_codec_controls(codec, ac101_controls, ARRAY_SIZE(ac101_controls)); ++ if (ret) { ++ pr_err("[ac10x] Failed to register audio mode control, " ++ "will continue without it.\n"); ++ } ++ ++ #ifdef CONFIG_AC101_SWITCH_DETECT ++ ret = ac101_switch_probe(ac10x); ++ if (ret) { ++ // not care the switch return value ++ } ++ #endif ++ ++ return 0; ++} ++ ++/* power down chip */ ++int ac101_codec_remove(struct snd_soc_codec *codec) ++{ ++ #ifdef CONFIG_AC101_SWITCH_DETECT ++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec); ++ ++ if (ac10x->irq) { ++ devm_free_irq(codec->dev, ac10x->irq, ac10x); ++ ac10x->irq = 0; ++ } ++ ++ if (cancel_work_sync(&ac10x->work_switch) != 0) { ++ } ++ ++ if (cancel_work_sync(&ac10x->work_clear_irq) != 0) { ++ } ++ ++ if (ac10x->inpdev) { ++ input_unregister_device(ac10x->inpdev); ++ ac10x->inpdev = NULL; ++ } ++ #endif ++ ++ return 0; ++} ++ ++int ac101_codec_suspend(struct snd_soc_codec *codec) ++{ ++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec); ++ ++ AC101_DBG("[codec]:suspend\n"); ++ regcache_cache_only(ac10x->regmap101, true); ++ return 0; ++} ++ ++int ac101_codec_resume(struct snd_soc_codec *codec) ++{ ++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec); ++ int ret; ++ ++ AC101_DBG("[codec]:resume"); ++ ++ /* Sync reg_cache with the hardware */ ++ regcache_cache_only(ac10x->regmap101, false); ++ ret = regcache_sync(ac10x->regmap101); ++ if (ret != 0) { ++ dev_err(codec->dev, "Failed to sync register cache: %d\n", ret); ++ regcache_cache_only(ac10x->regmap101, true); ++ return ret; ++ } ++ ++ #ifdef CONFIG_AC101_SWITCH_DETECT ++ ac10x->mode = HEADPHONE_IDLE; ++ ac10x->state = -1; ++ #endif ++ ++ ac101_set_bias_level(codec, SND_SOC_BIAS_STANDBY); ++ schedule_work(&ac10x->codec_resume); ++ return 0; ++} ++ ++/***************************************************************************/ ++static ssize_t ac101_debug_store(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t count) ++{ ++ struct ac10x_priv *ac10x = dev_get_drvdata(dev); ++ int val = 0, flag = 0; ++ u16 value_w, value_r; ++ u8 reg, num, i=0; ++ ++ val = simple_strtol(buf, NULL, 16); ++ flag = (val >> 24) & 0xF; ++ if (flag) { ++ reg = (val >> 16) & 0xFF; ++ value_w = val & 0xFFFF; ++ ac101_write(ac10x->codec, reg, value_w); ++ printk("write 0x%x to reg:0x%x\n", value_w, reg); ++ } else { ++ reg = (val >> 8) & 0xFF; ++ num = val & 0xff; ++ printk("\n"); ++ printk("read:start add:0x%x,count:0x%x\n", reg, num); ++ ++ regcache_cache_bypass(ac10x->regmap101, true); ++ do { ++ value_r = ac101_read(ac10x->codec, reg); ++ printk("0x%x: 0x%04x ", reg++, value_r); ++ if (++i % 4 == 0 || i == num) ++ printk("\n"); ++ } while (i < num); ++ regcache_cache_bypass(ac10x->regmap101, false); ++ } ++ return count; ++} ++static ssize_t ac101_debug_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ printk("echo flag|reg|val > ac10x\n"); ++ printk("eg read star addres=0x06,count 0x10:echo 0610 >ac10x\n"); ++ printk("eg write value:0x13fe to address:0x06 :echo 10613fe > ac10x\n"); ++ return 0; ++} ++static DEVICE_ATTR(ac10x, 0644, ac101_debug_show, ac101_debug_store); ++ ++static struct attribute *audio_debug_attrs[] = { ++ &dev_attr_ac10x.attr, ++ NULL, ++}; ++ ++static struct attribute_group audio_debug_attr_group = { ++ .name = "ac101_debug", ++ .attrs = audio_debug_attrs, ++}; ++/***************************************************************************/ ++ ++/************************************************************/ ++static bool ac101_volatile_reg(struct device *dev, unsigned int reg) ++{ ++ switch (reg) { ++ case PLL_CTRL2: ++ case HMIC_STS: ++ return true; ++ } ++ return false; ++} ++ ++static const struct regmap_config ac101_regmap = { ++ .reg_bits = 8, ++ .val_bits = 16, ++ .reg_stride = 1, ++ .max_register = 0xB5, ++ .cache_type = REGCACHE_FLAT, ++ .volatile_reg = ac101_volatile_reg, ++}; ++ ++/* Sync reg_cache from the hardware */ ++int ac10x_fill_regcache(struct device* dev, struct regmap* map) { ++ int r, i, n; ++ int v; ++ ++ n = regmap_get_max_register(map); ++ for (i = 0; i < n; i++) { ++ regcache_cache_bypass(map, true); ++ r = regmap_read(map, i, &v); ++ if (r) { ++ dev_dbg(dev, "failed to read register %d\n", i); ++ continue; ++ } ++ regcache_cache_bypass(map, false); ++ ++ regcache_cache_only(map, true); ++ r = regmap_write(map, i, v); ++ regcache_cache_only(map, false); ++ } ++ regcache_cache_bypass(map, false); ++ regcache_cache_only(map, false); ++ ++ return 0; ++} ++ ++int ac101_probe(struct i2c_client *i2c, const struct i2c_device_id *id) ++{ ++ struct ac10x_priv *ac10x = i2c_get_clientdata(i2c); ++ int ret = 0; ++ unsigned v = 0; ++ ++ AC101_DBG(); ++ ++ static_ac10x = ac10x; ++ ++ ac10x->regmap101 = devm_regmap_init_i2c(i2c, &ac101_regmap); ++ if (IS_ERR(ac10x->regmap101)) { ++ ret = PTR_ERR(ac10x->regmap101); ++ dev_err(&i2c->dev, "Fail to initialize I/O: %d\n", ret); ++ return ret; ++ } ++ ++ /* Chip reset */ ++ regcache_cache_only(ac10x->regmap101, false); ++ ret = regmap_write(ac10x->regmap101, CHIP_AUDIO_RST, 0); ++ msleep(50); ++ ++ /* sync regcache for FLAT type */ ++ ac10x_fill_regcache(&i2c->dev, ac10x->regmap101); ++ ++ ret = regmap_read(ac10x->regmap101, CHIP_AUDIO_RST, &v); ++ if (ret < 0) { ++ dev_err(&i2c->dev, "failed to read vendor ID: %d\n", ret); ++ return ret; ++ } ++ ++ if (v != AC101_CHIP_ID) { ++ dev_err(&i2c->dev, "chip is not AC101 (%X)\n", v); ++ dev_err(&i2c->dev, "Expected %X\n", AC101_CHIP_ID); ++ return -ENODEV; ++ } ++ ++ ret = sysfs_create_group(&i2c->dev.kobj, &audio_debug_attr_group); ++ if (ret) { ++ pr_err("failed to create attr group\n"); ++ } ++ ++ ac10x->gpiod_spk_amp_gate = devm_gpiod_get_optional(&i2c->dev, "spk-amp-switch", ++ GPIOD_OUT_LOW); ++ if (IS_ERR(ac10x->gpiod_spk_amp_gate)) { ++ ac10x->gpiod_spk_amp_gate = NULL; ++ dev_err(&i2c->dev, "failed get spk-amp-switch in device tree\n"); ++ } ++ ++ return 0; ++} ++ ++void ac101_shutdown(struct i2c_client *i2c) ++{ ++ struct ac10x_priv *ac10x = i2c_get_clientdata(i2c); ++ struct snd_soc_codec *codec = ac10x->codec; ++ int reg_val; ++ ++ if (codec == NULL) { ++ pr_err(": no sound card.\n"); ++ return; ++ } ++ ++ /*set headphone volume to 0*/ ++ reg_val = ac101_read(codec, HPOUT_CTRL); ++ reg_val &= ~(0x3f<dev.kobj, &audio_debug_attr_group); ++ return 0; ++} ++ ++MODULE_DESCRIPTION("ASoC ac10x driver"); ++MODULE_AUTHOR("huangxin,liushaohua"); ++MODULE_AUTHOR("PeterYang"); +--- /dev/null ++++ b/sound/soc/codecs/ac101_regs.h +@@ -0,0 +1,431 @@ ++/* ++ * ac101_regs.h ++ * ++ * (C) Copyright 2017-2018 ++ * Seeed Technology Co., Ltd. ++ * ++ * PeterYang ++ * ++ * (C) Copyright 2010-2017 ++ * Reuuimlla Technology Co., Ltd. ++ * huangxin ++ * ++ * some simple description for this code ++ * ++ * 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; either version 2 of ++ * the License, or (at your option) any later version. ++ * ++ */ ++#ifndef __AC101_REGS_H__ ++#define __AC101_REGS_H__ ++ ++/*pll source*/ ++#define AC101_MCLK1 1 ++#define AC101_MCLK2 2 ++#define AC101_BCLK1 3 ++#define AC101_BCLK2 4 ++ ++#define AIF1_CLK 1 ++#define AIF2_CLK 2 ++ ++#define CHIP_AUDIO_RST 0x0 ++#define PLL_CTRL1 0x1 ++#define PLL_CTRL2 0x2 ++#define SYSCLK_CTRL 0x3 ++#define MOD_CLK_ENA 0x4 ++#define MOD_RST_CTRL 0x5 ++#define AIF_SR_CTRL 0x6 ++ ++#define AIF1_CLK_CTRL 0x10 ++#define AIF1_ADCDAT_CTRL 0x11 ++#define AIF1_DACDAT_CTRL 0x12 ++#define AIF1_MXR_SRC 0x13 ++#define AIF1_VOL_CTRL1 0x14 ++#define AIF1_VOL_CTRL2 0x15 ++#define AIF1_VOL_CTRL3 0x16 ++#define AIF1_VOL_CTRL4 0x17 ++#define AIF1_MXR_GAIN 0x18 ++#define AIF1_RXD_CTRL 0x19 ++#define ADC_DIG_CTRL 0x40 ++#define ADC_VOL_CTRL 0x41 ++#define ADC_DBG_CTRL 0x42 ++ ++#define HMIC_CTRL1 0x44 ++#define HMIC_CTRL2 0x45 ++#define HMIC_STS 0x46 ++ ++#define DAC_DIG_CTRL 0x48 ++#define DAC_VOL_CTRL 0x49 ++#define DAC_DBG_CTRL 0x4a ++#define DAC_MXR_SRC 0x4c ++#define DAC_MXR_GAIN 0x4d ++ ++#define ADC_APC_CTRL 0x50 ++#define ADC_SRC 0x51 ++#define ADC_SRCBST_CTRL 0x52 ++#define OMIXER_DACA_CTRL 0x53 ++#define OMIXER_SR 0x54 ++#define OMIXER_BST1_CTRL 0x55 ++#define HPOUT_CTRL 0x56 ++#define ESPKOUT_CTRL 0x57 ++#define SPKOUT_CTRL 0x58 ++#define LOUT_CTRL 0x59 ++#define ADDA_TUNE1 0x5a ++#define ADDA_TUNE2 0x5b ++#define ADDA_TUNE3 0x5c ++#define HPOUT_STR 0x5d ++ ++/*CHIP_AUDIO_RST*/ ++#define AC101_CHIP_ID 0x0101 ++ ++/*PLL_CTRL1*/ ++#define DPLL_DAC_BIAS 14 ++#define PLL_POSTDIV_M 8 ++#define CLOSE_LOOP 6 ++#define INT 0 ++ ++/*PLL_CTRL2*/ ++#define PLL_EN 15 ++#define PLL_LOCK_STATUS 14 ++#define PLL_PREDIV_NI 4 ++#define PLL_POSTDIV_NF 0 ++ ++/*SYSCLK_CTRL*/ ++#define PLLCLK_ENA 15 ++#define PLLCLK_SRC 12 ++#define AIF1CLK_ENA 11 ++#define AIF1CLK_SRC 8 ++#define AIF2CLK_ENA 7 ++#define AIF2CLK_SRC 4 ++#define SYSCLK_ENA 3 ++#define SYSCLK_SRC 0 ++ ++/*MOD_CLK_ENA*/ ++#define MOD_CLK_AIF1 15 ++#define MOD_CLK_AIF2 14 ++#define MOD_CLK_AIF3 13 ++#define MOD_CLK_SRC1 11 ++#define MOD_CLK_SRC2 10 ++#define MOD_CLK_HPF_AGC 7 ++#define MOD_CLK_HPF_DRC 6 ++#define MOD_CLK_ADC_DIG 3 ++#define MOD_CLK_DAC_DIG 2 ++ ++/*MOD_RST_CTRL*/ ++#define MOD_RESET_CTL 0 ++#define MOD_RESET_AIF1 15 ++#define MOD_RESET_AIF2 14 ++#define MOD_RESET_AIF3 13 ++#define MOD_RESET_SRC1 11 ++#define MOD_RESET_SRC2 10 ++#define MOD_RESET_HPF_AGC 7 ++#define MOD_RESET_HPF_DRC 6 ++#define MOD_RESET_ADC_DIG 3 ++#define MOD_RESET_DAC_DIG 2 ++ ++/*AIF_SR_CTRL*/ ++#define AIF1_FS 12 //AIF1 Sample Rate ++#define AIF2_FS 8 //AIF2 Sample Rate ++#define SRC1_ENA 3 ++#define SRC1_SRC 2 ++#define SRC2_ENA 1 ++#define SRC2_SRC 0 ++ ++/*AIF1LCK_CTRL*/ ++#define AIF1_MSTR_MOD 15 ++#define AIF1_BCLK_INV 14 ++#define AIF1_LRCK_INV 13 ++#define AIF1_BCLK_DIV 9 ++#define AIF1_LRCK_DIV 6 ++#define AIF1_WORK_SIZ 4 ++#define AIF1_DATA_FMT 2 ++#define DSP_MONO_PCM 1 ++#define AIF1_TDMM_ENA 0 ++ ++/*AIF1_ADCDAT_CTRL*/ ++#define AIF1_AD0L_ENA 15 ++#define AIF1_AD0R_ENA 14 ++#define AIF1_AD1L_ENA 13 ++#define AIF1_AD1R_ENA 12 ++#define AIF1_AD0L_SRC 10 ++#define AIF1_AD0R_SRC 8 ++#define AIF1_AD1L_SRC 6 ++#define AIF1_AD1R_SRC 4 ++#define AIF1_ADCP_ENA 3 ++#define AIF1_ADUL_ENA 2 ++#define AIF1_SLOT_SIZ 0 ++ ++/*AIF1_DACDAT_CTRL*/ ++#define AIF1_DA0L_ENA 15 ++#define AIF1_DA0R_ENA 14 ++#define AIF1_DA1L_ENA 13 ++#define AIF1_DA1R_ENA 12 ++#define AIF1_DA0L_SRC 10 ++#define AIF1_DA0R_SRC 8 ++#define AIF1_DA1L_SRC 6 ++#define AIF1_DA1R_SRC 4 ++#define AIF1_DACP_ENA 3 ++#define AIF1_DAUL_ENA 2 ++#define AIF1_SLOT_SIZ 0 ++ ++/*AIF1_MXR_SRC*/ ++#define AIF1_AD0L_AIF1_DA0L_MXR 15 ++#define AIF1_AD0L_AIF2_DACL_MXR 14 ++#define AIF1_AD0L_ADCL_MXR 13 ++#define AIF1_AD0L_AIF2_DACR_MXR 12 ++#define AIF1_AD0R_AIF1_DA0R_MXR 11 ++#define AIF1_AD0R_AIF2_DACR_MXR 10 ++#define AIF1_AD0R_ADCR_MXR 9 ++#define AIF1_AD0R_AIF2_DACL_MXR 8 ++#define AIF1_AD1L_AIF2_DACL_MXR 7 ++#define AIF1_AD1L_ADCL_MXR 6 ++#define AIF1_AD1L_MXR_SRC 6 ++#define AIF1_AD1R_AIF2_DACR_MXR 3 ++#define AIF1_AD1R_ADCR_MXR 2 ++#define AIF1_AD1R_MXR_SRC 2 ++ ++/*AIF1_VOL_CTRL1*/ ++#define AIF1_AD0L_VOL 8 ++#define AIF1_AD0R_VOL 0 ++ ++/*AIF1_VOL_CTRL2*/ ++#define AIF1_AD1L_VOL 8 ++#define AIF1_AD1R_VOL 0 ++ ++/*AIF1_VOL_CTRL3*/ ++#define AIF1_DA0L_VOL 8 ++#define AIF1_DA0R_VOL 0 ++ ++/*AIF1_VOL_CTRL4*/ ++#define AIF1_DA1L_VOL 8 ++#define AIF1_DA1R_VOL 0 ++ ++/*AIF1_MXR_GAIN*/ ++#define AIF1_AD0L_MXR_GAIN 12 ++#define AIF1_AD0R_MXR_GAIN 8 ++#define AIF1_AD1L_MXR_GAIN 6 ++#define AIF1_AD1R_MXR_GAIN 2 ++ ++/*AIF1_RXD_CTRL*/ ++#define AIF1_N_DATA_DISCARD 8 ++ ++/*ADC_DIG_CTRL*/ ++#define ENAD 15 ++#define ENDM 14 ++#define ADFIR32 13 ++#define ADOUT_DTS 2 ++#define ADOUT_DLY 1 ++ ++/*ADC_VOL_CTRL*/ ++#define ADC_VOL_L 8 ++#define ADC_VOL_R 0 ++ ++/*ADC_DBG_CTRL*/ ++#define ADSW 15 ++#define DMIC_CLK_PIN_CTRL 12 ++ ++/*HMIC_CTRL1*/ ++#define HMIC_M 12 ++#define HMIC_N 8 ++#define HMIC_DATA_IRQ_MODE 7 ++#define HMIC_TH1_HYSTERESIS 5 ++#define HMIC_PULLOUT_IRQ 4 ++#define HMIC_PLUGIN_IRQ 3 ++#define HMIC_KEYUP_IRQ 2 ++#define HMIC_KEYDOWN_IRQ 1 ++#define HMIC_DATA_IRQ_EN 0 ++ ++/*HMIC_CTRL2*/ ++#define HMIC_SAMPLE_SELECT 14 ++#define HMIC_TH2_HYSTERESIS 13 ++#define HMIC_TH2 8 ++#define HMIC_SF 6 ++#define KEYUP_CLEAR 5 ++#define HMIC_TH1 0 ++ ++/*HMIC_STS*/ ++#define HMIC_DATA 8 ++#define GET_HMIC_DATA(r) (((r) >> HMIC_DATA) & 0x1F) ++#define HMIC_PULLOUT_PEND 4 ++#define HMIC_PLUGIN_PEND 3 ++#define HMIC_KEYUP_PEND 2 ++#define HMKC_KEYDOWN_PEND 1 ++#define HMIC_DATA_PEND 0 ++#define HMIC_PEND_ALL (0x1F) ++ ++/*DAC_DIG_CTRL*/ ++#define ENDA 15 ++#define ENHPF 14 ++#define DAFIR32 13 ++#define MODQU 8 ++ ++/*DAC_VOL_CTRL*/ ++#define DAC_VOL_L 8 ++#define DAC_VOL_R 0 ++ ++/*DAC_DBG_CTRL*/ ++#define DASW 15 ++#define ENDWA_N 14 ++#define DAC_MOD_DBG 13 ++#define DAC_PTN_SEL 6 ++#define DVC 0 ++ ++/*DAC_MXR_SRC*/ ++#define DACL_MXR_AIF1_DA0L 15 ++#define DACL_MXR_AIF1_DA1L 14 ++#define DACL_MXR_AIF2_DACL 13 ++#define DACL_MXR_ADCL 12 ++#define DACL_MXR_SRC 12 ++#define DACR_MXR_AIF1_DA0R 11 ++#define DACR_MXR_AIF1_DA1R 10 ++#define DACR_MXR_AIF2_DACR 9 ++#define DACR_MXR_ADCR 8 ++#define DACR_MXR_SRC 8 ++ ++/*DAC_MXR_GAIN*/ ++#define DACL_MXR_GAIN 12 ++#define DACR_MXR_GAIN 8 ++ ++/*ADC_APC_CTRL*/ ++#define ADCREN 15 ++#define ADCRG 12 ++#define ADCLEN 11 ++#define ADCLG 8 ++#define MBIASEN 7 ++#define MMIC_BIAS_CHOP_EN 6 ++#define MMIC_BIAS_CHOP_CKS 4 ++#define HBIASMOD 2 ++#define HBIASEN 1 ++#define HBIASADCEN 0 ++ ++/*ADC_SRC*/ ++#define RADCMIXMUTEMIC1BOOST (13) ++#define RADCMIXMUTEMIC2BOOST (12) ++#define RADCMIXMUTELINEINLR (11) ++#define RADCMIXMUTELINEINR (10) ++#define RADCMIXMUTEAUXINR (9) ++#define RADCMIXMUTEROUTPUT (8) ++#define RADCMIXMUTELOUTPUT (7) ++#define LADCMIXMUTEMIC1BOOST (6) ++#define LADCMIXMUTEMIC2BOOST (5) ++#define LADCMIXMUTELINEINLR (4) ++#define LADCMIXMUTELINEINL (3) ++#define LADCMIXMUTEAUXINL (2) ++#define LADCMIXMUTELOUTPUT (1) ++#define LADCMIXMUTEROUTPUT (0) ++ ++/*ADC_SRCBST_CTRL*/ ++#define MIC1AMPEN 15 ++#define ADC_MIC1G 12 ++#define MIC2AMPEN 11 ++#define ADC_MIC2G 8 ++#define MIC2SLT 7 ++#define LINEIN_PREG 4 ++#define AUXI_PREG 0 ++ ++/*OMIXER_DACA_CTRL*/ ++#define DACAREN 15 ++#define DACALEN 14 ++#define RMIXEN 13 ++#define LMIXEN 12 ++#define HPOUTPUTENABLE 8 ++ ++/*OMIXER_SR*/ ++#define RMIXMUTEMIC1BOOST (13) ++#define RMIXMUTEMIC2BOOST (12) ++#define RMIXMUTELINEINLR (11) ++#define RMIXMUTELINEINR (10) ++#define RMIXMUTEAUXINR (9) ++#define RMIXMUTEDACR (8) ++#define RMIXMUTEDACL (7) ++#define LMIXMUTEMIC1BOOST (6) ++#define LMIXMUTEMIC2BOOST (5) ++#define LMIXMUTELINEINLR (4) ++#define LMIXMUTELINEINL (3) ++#define LMIXMUTEAUXINL (2) ++#define LMIXMUTEDACL (1) ++#define LMIXMUTEDACR (0) ++ ++/*OMIXER_BST1_CTRL*/ ++#define BIASVOLTAGE 12 ++#define AXG 9 ++#define OMIXER_MIC1G 6 ++#define OMIXER_MIC2G 3 ++#define LINEING 0 ++ ++/*HPOUT_CTRL*/ ++#define RHPS 15 ++#define LHPS 14 ++#define RHPPA_MUTE 13 ++#define LHPPA_MUTE 12 ++#define HPPA_EN 11 ++#define HP_VOL 4 ++#define HPPA_DEL 2 ++#define HPPA_IS 0 ++ ++/*ESPKOUT_CTRL*/ ++#define EAR_RAMP_TIME 11 ++#define ESPA_OUT_CURRENT 9 ++#define ESPSR 7 ++#define ESPPA_MUTE 6 ++#define ESPPA_EN 5 ++#define ESP_VOL 0 ++ ++/*SPKOUT_CTRL*/ ++#define HPCALICKS 13 ++#define RSPKS 12 ++#define RSPKINVEN 11 ++#define RSPK_EN 9 ++#define LSPKS 8 ++#define LSPKINVEN 7 ++#define LSPK_EN 5 ++#define SPK_VOL 0 ++ ++/*LOUT_CTRL*/ ++#define LINEOUTG 5 ++#define LINEOUTEN 4 ++#define LINEOUTS0 3 ++#define LINEOUTS1 2 ++#define LINEOUTS2 1 ++#define LINEOUTS3 0 ++ ++/*ADDA_TUNE1*/ ++#define CURRENT_TEST_SELECT 14 ++#define BIHE_CTRL 12 ++#define DITHER 11 ++#define DITHER_CLK 9 ++#define ZERO_CROSSOVER_EN 8 ++#define ZERO_CROSSOVER_TIME 7 ++#define EAR_SPEED_SELECT 6 ++#define REF_CHOPPEN_CKS 4 ++#define OPMIC_BIAS_CUR 0 ++ ++/*ADDA_TUNE2*/ ++#define OPDAC_BIAS_CUR 14 ++#define OPDRV_BIAS_CUR 12 ++#define OPMIX_BIAS_CUR 10 ++#define OPEAR_BIAS_CUR 8 ++#define OPVR_BIAS_CUR 6 ++#define OPAAF_BIAS_CUR 4 ++#define OPADC1_BIAS_CUR 2 ++#define OPADC2_BIAS_CUR 0 ++ ++/*ADDA_TUNE3*/ ++#define LDOEN 15 ++#define LDO_SEL 12 ++#define BIASCALIVERIFY 11 ++#define BIASMODE 10 ++#define BIASCALIDATA 9 ++#define OSCS 1 ++#define OSCEN 0 ++ ++/*HPOUT_STR*/ ++#define HPVL_SOFT_MOD 14 ++#define HPVL_STEP_CTRL 8 ++#define DACA_CHND_ENA 7 ++#define HPPA_MXRD_ENA 6 ++#define HPVL_CTRL_OUT 0 ++ ++#endif//__AC101_REGS_H__ +--- /dev/null ++++ b/sound/soc/codecs/ac108.c +@@ -0,0 +1,1622 @@ ++/* ++ * ac10x.c -- ac10x ALSA SoC Audio driver ++ * ++ * ++ * Author: Baozhu Zuo ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "ac108.h" ++#include "ac10x.h" ++ ++#define _USE_CAPTURE 1 ++#define _MASTER_INDEX 0 ++ ++/* #undef DEBUG ++ * use 'make DEBUG=1' to enable debugging ++ */ ++ ++/** ++ * TODO: ++ * 1, add PM API: ac108_suspend,ac108_resume ++ * 2,0x65-0x6a ++ * 3,0x76-0x79 high 4bit ++ */ ++struct pll_div { ++ unsigned int freq_in; ++ unsigned int freq_out; ++ unsigned int m1; ++ unsigned int m2; ++ unsigned int n; ++ unsigned int k1; ++ unsigned int k2; ++}; ++ ++static struct ac10x_priv *ac10x; ++ ++struct real_val_to_reg_val { ++ unsigned int real_val; ++ unsigned int reg_val; ++}; ++ ++static const struct real_val_to_reg_val ac108_sample_rate[] = { ++ { 8000, 0 }, ++ { 11025, 1 }, ++ { 12000, 2 }, ++ { 16000, 3 }, ++ { 22050, 4 }, ++ { 24000, 5 }, ++ { 32000, 6 }, ++ { 44100, 7 }, ++ { 48000, 8 }, ++ { 96000, 9 }, ++}; ++ ++/* Sample resolution */ ++static const struct real_val_to_reg_val ac108_samp_res[] = { ++ { 8, 1 }, ++ { 12, 2 }, ++ { 16, 3 }, ++ { 20, 4 }, ++ { 24, 5 }, ++ { 28, 6 }, ++ { 32, 7 }, ++}; ++ ++static const unsigned int ac108_bclkdivs[] = { ++ 0, 1, 2, 4, ++ 6, 8, 12, 16, ++ 24, 32, 48, 64, ++ 96, 128, 176, 192, ++}; ++ ++/* FOUT =(FIN * N) / [(M1+1) * (M2+1)*(K1+1)*(K2+1)] ; M1[0,31], M2[0,1], N[0,1023], K1[0,31], K2[0,1] */ ++static const struct pll_div ac108_pll_div_list[] = { ++ { 400000, _FREQ_24_576K, 0, 0, 614, 4, 1 }, ++ { 512000, _FREQ_24_576K, 0, 0, 960, 9, 1 }, /* _FREQ_24_576K/48 */ ++ { 768000, _FREQ_24_576K, 0, 0, 640, 9, 1 }, /* _FREQ_24_576K/32 */ ++ { 800000, _FREQ_24_576K, 0, 0, 614, 9, 1 }, ++ { 1024000, _FREQ_24_576K, 0, 0, 480, 9, 1 }, /* _FREQ_24_576K/24 */ ++ { 1600000, _FREQ_24_576K, 0, 0, 307, 9, 1 }, ++ { 2048000, _FREQ_24_576K, 0, 0, 240, 9, 1 }, /* accurate, 8000 * 256 */ ++ { 3072000, _FREQ_24_576K, 0, 0, 160, 9, 1 }, /* accurate, 12000 * 256 */ ++ { 4096000, _FREQ_24_576K, 2, 0, 360, 9, 1 }, /* accurate, 16000 * 256 */ ++ { 6000000, _FREQ_24_576K, 4, 0, 410, 9, 1 }, ++ { 12000000, _FREQ_24_576K, 9, 0, 410, 9, 1 }, ++ { 13000000, _FREQ_24_576K, 8, 0, 340, 9, 1 }, ++ { 15360000, _FREQ_24_576K, 12, 0, 415, 9, 1 }, ++ { 16000000, _FREQ_24_576K, 12, 0, 400, 9, 1 }, ++ { 19200000, _FREQ_24_576K, 15, 0, 410, 9, 1 }, ++ { 19680000, _FREQ_24_576K, 15, 0, 400, 9, 1 }, ++ { 24000000, _FREQ_24_576K, 9, 0, 256,24, 0 }, /* accurate, 24M -> 24.576M */ ++ ++ { 400000, _FREQ_22_579K, 0, 0, 566, 4, 1 }, ++ { 512000, _FREQ_22_579K, 0, 0, 880, 9, 1 }, ++ { 768000, _FREQ_22_579K, 0, 0, 587, 9, 1 }, ++ { 800000, _FREQ_22_579K, 0, 0, 567, 9, 1 }, ++ { 1024000, _FREQ_22_579K, 0, 0, 440, 9, 1 }, ++ { 1600000, _FREQ_22_579K, 1, 0, 567, 9, 1 }, ++ { 2048000, _FREQ_22_579K, 0, 0, 220, 9, 1 }, ++ { 3072000, _FREQ_22_579K, 0, 0, 148, 9, 1 }, ++ { 4096000, _FREQ_22_579K, 2, 0, 330, 9, 1 }, ++ { 6000000, _FREQ_22_579K, 2, 0, 227, 9, 1 }, ++ { 12000000, _FREQ_22_579K, 8, 0, 340, 9, 1 }, ++ { 13000000, _FREQ_22_579K, 9, 0, 350, 9, 1 }, ++ { 15360000, _FREQ_22_579K, 10, 0, 325, 9, 1 }, ++ { 16000000, _FREQ_22_579K, 11, 0, 340, 9, 1 }, ++ { 19200000, _FREQ_22_579K, 13, 0, 330, 9, 1 }, ++ { 19680000, _FREQ_22_579K, 14, 0, 345, 9, 1 }, ++ { 24000000, _FREQ_22_579K, 24, 0, 588,24, 0 }, /* accurate, 24M -> 22.5792M */ ++ ++ ++ { _FREQ_24_576K / 1, _FREQ_24_576K, 9, 0, 200, 9, 1 }, /* _FREQ_24_576K */ ++ { _FREQ_24_576K / 2, _FREQ_24_576K, 9, 0, 400, 9, 1 }, /* 12288000,accurate, 48000 * 256 */ ++ { _FREQ_24_576K / 4, _FREQ_24_576K, 4, 0, 400, 9, 1 }, /* 6144000, accurate, 24000 * 256 */ ++ { _FREQ_24_576K / 16, _FREQ_24_576K, 0, 0, 320, 9, 1 }, /* 1536000 */ ++ { _FREQ_24_576K / 64, _FREQ_24_576K, 0, 0, 640, 4, 1 }, /* 384000 */ ++ { _FREQ_24_576K / 96, _FREQ_24_576K, 0, 0, 960, 4, 1 }, /* 256000 */ ++ { _FREQ_24_576K / 128, _FREQ_24_576K, 0, 0, 512, 1, 1 }, /* 192000 */ ++ { _FREQ_24_576K / 176, _FREQ_24_576K, 0, 0, 880, 4, 0 }, /* 140000 */ ++ { _FREQ_24_576K / 192, _FREQ_24_576K, 0, 0, 960, 4, 0 }, /* 128000 */ ++ ++ { _FREQ_22_579K / 1, _FREQ_22_579K, 9, 0, 200, 9, 1 }, /* _FREQ_22_579K */ ++ { _FREQ_22_579K / 2, _FREQ_22_579K, 9, 0, 400, 9, 1 }, /* 11289600,accurate, 44100 * 256 */ ++ { _FREQ_22_579K / 4, _FREQ_22_579K, 4, 0, 400, 9, 1 }, /* 5644800, accurate, 22050 * 256 */ ++ { _FREQ_22_579K / 16, _FREQ_22_579K, 0, 0, 320, 9, 1 }, /* 1411200 */ ++ { _FREQ_22_579K / 64, _FREQ_22_579K, 0, 0, 640, 4, 1 }, /* 352800 */ ++ { _FREQ_22_579K / 96, _FREQ_22_579K, 0, 0, 960, 4, 1 }, /* 235200 */ ++ { _FREQ_22_579K / 128, _FREQ_22_579K, 0, 0, 512, 1, 1 }, /* 176400 */ ++ { _FREQ_22_579K / 176, _FREQ_22_579K, 0, 0, 880, 4, 0 }, /* 128290 */ ++ { _FREQ_22_579K / 192, _FREQ_22_579K, 0, 0, 960, 4, 0 }, /* 117600 */ ++ ++ { _FREQ_22_579K / 6, _FREQ_22_579K, 2, 0, 360, 9, 1 }, /* 3763200 */ ++ { _FREQ_22_579K / 8, _FREQ_22_579K, 0, 0, 160, 9, 1 }, /* 2822400, accurate, 11025 * 256 */ ++ { _FREQ_22_579K / 12, _FREQ_22_579K, 0, 0, 240, 9, 1 }, /* 1881600 */ ++ { _FREQ_22_579K / 24, _FREQ_22_579K, 0, 0, 480, 9, 1 }, /* 940800 */ ++ { _FREQ_22_579K / 32, _FREQ_22_579K, 0, 0, 640, 9, 1 }, /* 705600 */ ++ { _FREQ_22_579K / 48, _FREQ_22_579K, 0, 0, 960, 9, 1 }, /* 470400 */ ++}; ++ ++ ++/* AC108 definition */ ++#define AC108_CHANNELS_MAX 8 /* range[1, 16] */ ++#define AC108_RATES (SNDRV_PCM_RATE_8000_96000 & \ ++ ~(SNDRV_PCM_RATE_64000 | \ ++ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)) ++#define AC108_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ ++ /*SNDRV_PCM_FMTBIT_S20_3LE | \ ++ SNDRV_PCM_FMTBIT_S24_LE |*/ \ ++ SNDRV_PCM_FMTBIT_S32_LE) ++ ++static const DECLARE_TLV_DB_SCALE(tlv_adc_pga_gain, 0, 100, 0); ++static const DECLARE_TLV_DB_SCALE(tlv_ch_digital_vol, -11925, 75, 0); ++ ++int ac10x_read(u8 reg, u8* rt_val, struct regmap* i2cm) ++{ ++ int r, v = 0; ++ ++ if ((r = regmap_read(i2cm, reg, &v)) < 0) ++ pr_info("ac10x_read info->[REG-0x%02x]\n", reg); ++ else ++ *rt_val = v; ++ return r; ++} ++ ++int ac10x_write(u8 reg, u8 val, struct regmap* i2cm) ++{ ++ int r; ++ ++ if ((r = regmap_write(i2cm, reg, val)) < 0) ++ pr_info("ac10x_write info->[REG-0x%02x,val-0x%02x]\n", reg, val); ++ return r; ++} ++ ++int ac10x_update_bits(u8 reg, u8 mask, u8 val, struct regmap* i2cm) ++{ ++ int r; ++ ++ if ((r = regmap_update_bits(i2cm, reg, mask, val)) < 0) ++ pr_info("%s() info->[REG-0x%02x,val-0x%02x]\n", __func__, reg, val); ++ return r; ++} ++ ++/** ++ * snd_ac108_get_volsw - single mixer get callback ++ * @kcontrol: mixer control ++ * @ucontrol: control element information ++ * ++ * Callback to get the value of a single mixer control, or a double mixer ++ * control that spans 2 registers. ++ * ++ * Returns 0 for success. ++ */ ++static int snd_ac108_get_volsw(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct soc_mixer_control *mc = ++ (struct soc_mixer_control *)kcontrol->private_value; ++ unsigned int mask = (1 << fls(mc->max)) - 1; ++ unsigned int invert = mc->invert; ++ int ret, chip = mc->autodisable; ++ u8 val; ++ ++ if ((ret = ac10x_read(mc->reg, &val, ac10x->i2cmap[chip])) < 0) ++ return ret; ++ ++ val = ((val >> mc->shift) & mask) - mc->min; ++ if (invert) { ++ val = mc->max - val; ++ } ++ ucontrol->value.integer.value[0] = val; ++ return 0; ++} ++ ++/** ++ * snd_ac108_put_volsw - single mixer put callback ++ * @kcontrol: mixer control ++ * @ucontrol: control element information ++ * ++ * Callback to set the value of a single mixer control, or a double mixer ++ * control that spans 2 registers. ++ * ++ * Returns 0 for success. ++ */ ++static int snd_ac108_put_volsw(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct soc_mixer_control *mc = ++ (struct soc_mixer_control *)kcontrol->private_value; ++ unsigned int sign_bit = mc->sign_bit; ++ unsigned int val, mask = (1 << fls(mc->max)) - 1; ++ unsigned int invert = mc->invert; ++ int ret, chip = mc->autodisable; ++ ++ if (sign_bit) ++ mask = BIT(sign_bit + 1) - 1; ++ ++ val = ((ucontrol->value.integer.value[0] + mc->min) & mask); ++ if (invert) { ++ val = mc->max - val; ++ } ++ ++ mask = mask << mc->shift; ++ val = val << mc->shift; ++ ++ ret = ac10x_update_bits(mc->reg, mask, val, ac10x->i2cmap[chip]); ++ return ret; ++} ++ ++#define SOC_AC108_SINGLE_TLV(xname, reg, shift, max, invert, chip, tlv_array) \ ++{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ ++ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ ++ SNDRV_CTL_ELEM_ACCESS_READWRITE,\ ++ .tlv.p = (tlv_array), \ ++ .info = snd_soc_info_volsw, .get = snd_ac108_get_volsw,\ ++ .put = snd_ac108_put_volsw, \ ++ .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, chip) } ++ ++/* single ac108 */ ++static const struct snd_kcontrol_new ac108_snd_controls[] = { ++ /* ### chip 0 ### */ ++ /*0x70: ADC1 Digital Channel Volume Control Register*/ ++ SOC_AC108_SINGLE_TLV("CH1 digital volume", ADC1_DVOL_CTRL, ++ 0, 0xff, 0, 0, tlv_ch_digital_vol), ++ /*0x71: ADC2 Digital Channel Volume Control Register*/ ++ SOC_AC108_SINGLE_TLV("CH2 digital volume", ADC2_DVOL_CTRL, ++ 0, 0xff, 0, 0, tlv_ch_digital_vol), ++ /*0x72: ADC3 Digital Channel Volume Control Register*/ ++ SOC_AC108_SINGLE_TLV("CH3 digital volume", ADC3_DVOL_CTRL, ++ 0, 0xff, 0, 0, tlv_ch_digital_vol), ++ /*0x73: ADC4 Digital Channel Volume Control Register*/ ++ SOC_AC108_SINGLE_TLV("CH4 digital volume", ADC4_DVOL_CTRL, ++ 0, 0xff, 0, 0, tlv_ch_digital_vol), ++ ++ /*0x90: Analog PGA1 Control Register*/ ++ SOC_AC108_SINGLE_TLV("ADC1 PGA gain", ANA_PGA1_CTRL, ++ ADC1_ANALOG_PGA, 0x1f, 0, 0, tlv_adc_pga_gain), ++ /*0x91: Analog PGA2 Control Register*/ ++ SOC_AC108_SINGLE_TLV("ADC2 PGA gain", ANA_PGA2_CTRL, ++ ADC2_ANALOG_PGA, 0x1f, 0, 0, tlv_adc_pga_gain), ++ /*0x92: Analog PGA3 Control Register*/ ++ SOC_AC108_SINGLE_TLV("ADC3 PGA gain", ANA_PGA3_CTRL, ++ ADC3_ANALOG_PGA, 0x1f, 0, 0, tlv_adc_pga_gain), ++ /*0x93: Analog PGA4 Control Register*/ ++ SOC_AC108_SINGLE_TLV("ADC4 PGA gain", ANA_PGA4_CTRL, ++ ADC4_ANALOG_PGA, 0x1f, 0, 0, tlv_adc_pga_gain), ++}; ++/* multiple ac108s */ ++static const struct snd_kcontrol_new ac108tdm_snd_controls[] = { ++ /* ### chip 1 ### */ ++ /*0x70: ADC1 Digital Channel Volume Control Register*/ ++ SOC_AC108_SINGLE_TLV("CH1 digital volume", ADC1_DVOL_CTRL, ++ 0, 0xff, 0, 1, tlv_ch_digital_vol), ++ /*0x71: ADC2 Digital Channel Volume Control Register*/ ++ SOC_AC108_SINGLE_TLV("CH2 digital volume", ADC2_DVOL_CTRL, ++ 0, 0xff, 0, 1, tlv_ch_digital_vol), ++ /*0x72: ADC3 Digital Channel Volume Control Register*/ ++ SOC_AC108_SINGLE_TLV("CH3 digital volume", ADC3_DVOL_CTRL, ++ 0, 0xff, 0, 1, tlv_ch_digital_vol), ++ /*0x73: ADC4 Digital Channel Volume Control Register*/ ++ SOC_AC108_SINGLE_TLV("CH4 digital volume", ADC4_DVOL_CTRL, ++ 0, 0xff, 0, 1, tlv_ch_digital_vol), ++ ++ /*0x90: Analog PGA1 Control Register*/ ++ SOC_AC108_SINGLE_TLV("ADC1 PGA gain", ANA_PGA1_CTRL, ++ ADC1_ANALOG_PGA, 0x1f, 0, 1, tlv_adc_pga_gain), ++ /*0x91: Analog PGA2 Control Register*/ ++ SOC_AC108_SINGLE_TLV("ADC2 PGA gain", ANA_PGA2_CTRL, ++ ADC2_ANALOG_PGA, 0x1f, 0, 1, tlv_adc_pga_gain), ++ /*0x92: Analog PGA3 Control Register*/ ++ SOC_AC108_SINGLE_TLV("ADC3 PGA gain", ANA_PGA3_CTRL, ++ ADC3_ANALOG_PGA, 0x1f, 0, 1, tlv_adc_pga_gain), ++ /*0x93: Analog PGA4 Control Register*/ ++ SOC_AC108_SINGLE_TLV("ADC4 PGA gain", ANA_PGA4_CTRL, ++ ADC4_ANALOG_PGA, 0x1f, 0, 1, tlv_adc_pga_gain), ++ ++ /* ### chip 0 ### */ ++ /*0x70: ADC1 Digital Channel Volume Control Register*/ ++ SOC_AC108_SINGLE_TLV("CH5 digital volume", ADC1_DVOL_CTRL, ++ 0, 0xff, 0, 0, tlv_ch_digital_vol), ++ /*0x71: ADC2 Digital Channel Volume Control Register*/ ++ SOC_AC108_SINGLE_TLV("CH6 digital volume", ADC2_DVOL_CTRL, ++ 0, 0xff, 0, 0, tlv_ch_digital_vol), ++ /*0x72: ADC3 Digital Channel Volume Control Register*/ ++ SOC_AC108_SINGLE_TLV("CH7 digital volume", ADC3_DVOL_CTRL, ++ 0, 0xff, 0, 0, tlv_ch_digital_vol), ++ /*0x73: ADC4 Digital Channel Volume Control Register*/ ++ SOC_AC108_SINGLE_TLV("CH8 digital volume", ADC4_DVOL_CTRL, ++ 0, 0xff, 0, 0, tlv_ch_digital_vol), ++ ++ /*0x90: Analog PGA1 Control Register*/ ++ SOC_AC108_SINGLE_TLV("ADC5 PGA gain", ANA_PGA1_CTRL, ++ ADC1_ANALOG_PGA, 0x1f, 0, 0, tlv_adc_pga_gain), ++ /*0x91: Analog PGA2 Control Register*/ ++ SOC_AC108_SINGLE_TLV("ADC6 PGA gain", ANA_PGA2_CTRL, ++ ADC2_ANALOG_PGA, 0x1f, 0, 0, tlv_adc_pga_gain), ++ /*0x92: Analog PGA3 Control Register*/ ++ SOC_AC108_SINGLE_TLV("ADC7 PGA gain", ANA_PGA3_CTRL, ++ ADC3_ANALOG_PGA, 0x1f, 0, 0, tlv_adc_pga_gain), ++ /*0x93: Analog PGA4 Control Register*/ ++ SOC_AC108_SINGLE_TLV("ADC8 PGA gain", ANA_PGA4_CTRL, ++ ADC4_ANALOG_PGA, 0x1f, 0, 0, tlv_adc_pga_gain), ++}; ++ ++ ++static const struct snd_soc_dapm_widget ac108_dapm_widgets[] = { ++ /* input widgets */ ++ SND_SOC_DAPM_INPUT("MIC1P"), ++ SND_SOC_DAPM_INPUT("MIC1N"), ++ ++ SND_SOC_DAPM_INPUT("MIC2P"), ++ SND_SOC_DAPM_INPUT("MIC2N"), ++ ++ SND_SOC_DAPM_INPUT("MIC3P"), ++ SND_SOC_DAPM_INPUT("MIC3N"), ++ ++ SND_SOC_DAPM_INPUT("MIC4P"), ++ SND_SOC_DAPM_INPUT("MIC4N"), ++ ++ SND_SOC_DAPM_INPUT("DMIC1"), ++ SND_SOC_DAPM_INPUT("DMIC2"), ++ ++ /*0xa0: ADC1 Analog Control 1 Register*/ ++ /*0xa1-0xa6:use the defualt value*/ ++ SND_SOC_DAPM_AIF_IN("Channel 1 AAF", "Capture", 0, ANA_ADC1_CTRL1, ADC1_DSM_ENABLE, 1), ++ SND_SOC_DAPM_SUPPLY("Channel 1 EN", ANA_ADC1_CTRL1, ADC1_PGA_ENABLE, 1, NULL, 0), ++ SND_SOC_DAPM_MICBIAS("MIC1BIAS", ANA_ADC1_CTRL1, ADC1_MICBIAS_EN, 1), ++ ++ /*0xa7: ADC2 Analog Control 1 Register*/ ++ /*0xa8-0xad:use the defualt value*/ ++ SND_SOC_DAPM_AIF_IN("Channel 2 AAF", "Capture", 0, ANA_ADC2_CTRL1, ADC2_DSM_ENABLE, 1), ++ SND_SOC_DAPM_SUPPLY("Channel 2 EN", ANA_ADC2_CTRL1, ADC2_PGA_ENABLE, 1, NULL, 0), ++ SND_SOC_DAPM_MICBIAS("MIC2BIAS", ANA_ADC2_CTRL1, ADC2_MICBIAS_EN, 1), ++ ++ /*0xae: ADC3 Analog Control 1 Register*/ ++ /*0xaf-0xb4:use the defualt value*/ ++ SND_SOC_DAPM_AIF_IN("Channel 3 AAF", "Capture", 0, ANA_ADC3_CTRL1, ADC3_DSM_ENABLE, 1), ++ SND_SOC_DAPM_SUPPLY("Channel 3 EN", ANA_ADC3_CTRL1, ADC3_PGA_ENABLE, 1, NULL, 0), ++ SND_SOC_DAPM_MICBIAS("MIC3BIAS", ANA_ADC3_CTRL1, ADC3_MICBIAS_EN, 1), ++ ++ /*0xb5: ADC4 Analog Control 1 Register*/ ++ /*0xb6-0xbb:use the defualt value*/ ++ SND_SOC_DAPM_AIF_IN("Channel 4 AAF", "Capture", 0, ANA_ADC4_CTRL1, ADC4_DSM_ENABLE, 1), ++ SND_SOC_DAPM_SUPPLY("Channel 4 EN", ANA_ADC4_CTRL1, ADC4_PGA_ENABLE, 1, NULL, 0), ++ SND_SOC_DAPM_MICBIAS("MIC4BIAS", ANA_ADC4_CTRL1, ADC4_MICBIAS_EN, 1), ++ ++ ++ /*0x61: ADC Digital Part Enable Register*/ ++ SND_SOC_DAPM_SUPPLY("ADC EN", ADC_DIG_EN, 4, 1, NULL, 0), ++ SND_SOC_DAPM_ADC("ADC1", "Capture", ADC_DIG_EN, 0, 1), ++ SND_SOC_DAPM_ADC("ADC2", "Capture", ADC_DIG_EN, 1, 1), ++ SND_SOC_DAPM_ADC("ADC3", "Capture", ADC_DIG_EN, 2, 1), ++ SND_SOC_DAPM_ADC("ADC4", "Capture", ADC_DIG_EN, 3, 1), ++ ++ SND_SOC_DAPM_SUPPLY("ADC1 CLK", ANA_ADC4_CTRL7, ADC1_CLK_GATING, 1, NULL, 0), ++ SND_SOC_DAPM_SUPPLY("ADC2 CLK", ANA_ADC4_CTRL7, ADC2_CLK_GATING, 1, NULL, 0), ++ SND_SOC_DAPM_SUPPLY("ADC3 CLK", ANA_ADC4_CTRL7, ADC3_CLK_GATING, 1, NULL, 0), ++ SND_SOC_DAPM_SUPPLY("ADC4 CLK", ANA_ADC4_CTRL7, ADC4_CLK_GATING, 1, NULL, 0), ++ ++ SND_SOC_DAPM_SUPPLY("DSM EN", ANA_ADC4_CTRL6, DSM_DEMOFF, 1, NULL, 0), ++ ++ /*0x62:Digital MIC Enable Register*/ ++ SND_SOC_DAPM_MICBIAS("DMIC1 enable", DMIC_EN, 0, 0), ++ SND_SOC_DAPM_MICBIAS("DMIC2 enable", DMIC_EN, 1, 0), ++}; ++ ++static const struct snd_soc_dapm_route ac108_dapm_routes[] = { ++ ++ { "ADC1", NULL, "Channel 1 AAF" }, ++ { "ADC2", NULL, "Channel 2 AAF" }, ++ { "ADC3", NULL, "Channel 3 AAF" }, ++ { "ADC4", NULL, "Channel 4 AAF" }, ++ ++ { "Channel 1 AAF", NULL, "MIC1BIAS" }, ++ { "Channel 2 AAF", NULL, "MIC2BIAS" }, ++ { "Channel 3 AAF", NULL, "MIC3BIAS" }, ++ { "Channel 4 AAF", NULL, "MIC4BIAS" }, ++ ++ { "MIC1BIAS", NULL, "ADC1 CLK" }, ++ { "MIC2BIAS", NULL, "ADC2 CLK" }, ++ { "MIC3BIAS", NULL, "ADC3 CLK" }, ++ { "MIC4BIAS", NULL, "ADC4 CLK" }, ++ ++ ++ { "ADC1 CLK", NULL, "DSM EN" }, ++ { "ADC2 CLK", NULL, "DSM EN" }, ++ { "ADC3 CLK", NULL, "DSM EN" }, ++ { "ADC4 CLK", NULL, "DSM EN" }, ++ ++ ++ { "DSM EN", NULL, "ADC EN" }, ++ ++ { "Channel 1 EN", NULL, "DSM EN" }, ++ { "Channel 2 EN", NULL, "DSM EN" }, ++ { "Channel 3 EN", NULL, "DSM EN" }, ++ { "Channel 4 EN", NULL, "DSM EN" }, ++ ++ ++ { "MIC1P", NULL, "Channel 1 EN" }, ++ { "MIC1N", NULL, "Channel 1 EN" }, ++ ++ { "MIC2P", NULL, "Channel 2 EN" }, ++ { "MIC2N", NULL, "Channel 2 EN" }, ++ ++ { "MIC3P", NULL, "Channel 3 EN" }, ++ { "MIC3N", NULL, "Channel 3 EN" }, ++ ++ { "MIC4P", NULL, "Channel 4 EN" }, ++ { "MIC4N", NULL, "Channel 4 EN" }, ++ ++}; ++ ++static int ac108_multi_write(u8 reg, u8 val, struct ac10x_priv *ac10x) ++{ ++ u8 i; ++ for (i = 0; i < ac10x->codec_cnt; i++) ++ ac10x_write(reg, val, ac10x->i2cmap[i]); ++ return 0; ++} ++ ++static int ac108_multi_update_bits(u8 reg, u8 mask, u8 val, struct ac10x_priv *ac10x) ++{ ++ int r = 0; ++ u8 i; ++ for (i = 0; i < ac10x->codec_cnt; i++) ++ r |= ac10x_update_bits(reg, mask, val, ac10x->i2cmap[i]); ++ return r; ++} ++ ++static unsigned int ac108_codec_read(struct snd_soc_codec *codec, unsigned int reg) ++{ ++ unsigned char val_r; ++ struct ac10x_priv *ac10x = dev_get_drvdata(codec->dev); ++ /*read one chip is fine*/ ++ ac10x_read(reg, &val_r, ac10x->i2cmap[_MASTER_INDEX]); ++ return val_r; ++} ++ ++static int ac108_codec_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int val) ++{ ++ struct ac10x_priv *ac10x = dev_get_drvdata(codec->dev); ++ ac108_multi_write(reg, val, ac10x); ++ return 0; ++} ++ ++/** ++ * The Power management related registers are Reg01h~Reg09h ++ * 0x01-0x05,0x08,use the default value ++ * @author baozhu (17-6-21) ++ * ++ * @param ac10x ++ */ ++static void ac108_configure_power(struct ac10x_priv *ac10x) ++{ ++ /** ++ * 0x06:Enable Analog LDO ++ */ ++ ac108_multi_update_bits(PWR_CTRL6, 0x01 << LDO33ANA_ENABLE, 0x01 << LDO33ANA_ENABLE, ac10x); ++ /** ++ * 0x07: ++ * Control VREF output and micbias voltage ? ++ * REF faststart disable, enable Enable VREF (needed for Analog ++ * LDO and MICBIAS) ++ */ ++ ac108_multi_update_bits(PWR_CTRL7, ++ 0x1f << VREF_SEL | 0x01 << VREF_FASTSTART_ENABLE | ++ 0x01 << VREF_ENABLE, ++ 0x13 << VREF_SEL | 0x00 << VREF_FASTSTART_ENABLE | ++ 0x01 << VREF_ENABLE, ++ ac10x); ++ /** ++ * 0x09: ++ * Disable fast-start circuit on VREFP ++ * VREFP_RESCTRL=00=1 MOhm ++ * IGEN_TRIM=100=+25% ++ * Enable VREFP (needed by all audio input channels) ++ */ ++ ac108_multi_update_bits(PWR_CTRL9, ++ 0x01 << VREFP_FASTSTART_ENABLE | 0x03 << VREFP_RESCTRL | ++ 0x07 << IGEN_TRIM | 0x01 << VREFP_ENABLE, ++ 0x00 << VREFP_FASTSTART_ENABLE | 0x00 << VREFP_RESCTRL | ++ 0x04 << IGEN_TRIM | 0x01 << VREFP_ENABLE, ++ ac10x); ++} ++ ++/** ++ * The clock management related registers are Reg20h~Reg25h ++ * The PLL management related registers are Reg10h~Reg18h. ++ * @author baozhu (17-6-20) ++ * ++ * @param ac10x ++ * @param rate : sample rate ++ * ++ * @return int : fail or success ++ */ ++static int ac108_config_pll(struct ac10x_priv *ac10x, unsigned rate, unsigned lrck_ratio) ++{ ++ unsigned int i = 0; ++ struct pll_div ac108_pll_div = { 0 }; ++ ++ if (ac10x->clk_id == SYSCLK_SRC_PLL) { ++ unsigned pll_src, pll_freq_in; ++ ++ if (lrck_ratio == 0) { ++ /* PLL clock source from MCLK */ ++ pll_freq_in = ac10x->sysclk; ++ pll_src = 0x0; ++ } else { ++ /* PLL clock source from BCLK */ ++ pll_freq_in = rate * lrck_ratio; ++ pll_src = 0x1; ++ } ++ ++ /* FOUT =(FIN * N) / [(M1+1) * (M2+1)*(K1+1)*(K2+1)] */ ++ for (i = 0; i < ARRAY_SIZE(ac108_pll_div_list); i++) { ++ if (ac108_pll_div_list[i].freq_in == pll_freq_in && ++ ac108_pll_div_list[i].freq_out % rate == 0) { ++ ac108_pll_div = ac108_pll_div_list[i]; ++ dev_info(&ac10x->i2c[_MASTER_INDEX]->dev, ++ "AC108 PLL freq_in match:%u, freq_out:%u\n\n", ++ ac108_pll_div.freq_in, ac108_pll_div.freq_out); ++ break; ++ } ++ } ++ /* 0x11,0x12,0x13,0x14: Config PLL DIV param M1/M2/N/K1/K2 */ ++ ac108_multi_update_bits(PLL_CTRL5, ++ 0x1f << PLL_POSTDIV1 | 0x01 << PLL_POSTDIV2, ++ ac108_pll_div.k1 << PLL_POSTDIV1 | ++ ac108_pll_div.k2 << PLL_POSTDIV2, ac10x); ++ ac108_multi_update_bits(PLL_CTRL4, 0xff << PLL_LOOPDIV_LSB, ++ (unsigned char)ac108_pll_div.n << PLL_LOOPDIV_LSB, ac10x); ++ ac108_multi_update_bits(PLL_CTRL3, 0x03 << PLL_LOOPDIV_MSB, ++ (ac108_pll_div.n >> 8) << PLL_LOOPDIV_MSB, ac10x); ++ ac108_multi_update_bits(PLL_CTRL2, 0x1f << PLL_PREDIV1 | 0x01 << PLL_PREDIV2, ++ ac108_pll_div.m1 << PLL_PREDIV1 | ++ ac108_pll_div.m2 << PLL_PREDIV2, ac10x); ++ ++ /*0x18: PLL clk lock enable*/ ++ ac108_multi_update_bits(PLL_LOCK_CTRL, 0x1 << PLL_LOCK_EN, ++ 0x1 << PLL_LOCK_EN, ac10x); ++ ++ /** ++ * 0x20: enable pll, pll source from mclk/bclk, sysclk source from pll, enable sysclk ++ */ ++ ac108_multi_update_bits(SYSCLK_CTRL, ++ 0x01 << PLLCLK_EN | 0x03 << PLLCLK_SRC | ++ 0x01 << SYSCLK_SRC | 0x01 << SYSCLK_EN, ++ 0x01 << PLLCLK_EN | pll_src << PLLCLK_SRC | ++ 0x01 << SYSCLK_SRC | 0x01 << SYSCLK_EN, ac10x); ++ ac10x->mclk = ac108_pll_div.freq_out; ++ } ++ if (ac10x->clk_id == SYSCLK_SRC_MCLK) { ++ /** ++ *0x20: sysclk source from mclk, enable sysclk ++ */ ++ ac108_multi_update_bits(SYSCLK_CTRL, ++ 0x01 << PLLCLK_EN | 0x01 << SYSCLK_SRC | 0x01 << SYSCLK_EN, ++ 0x00 << PLLCLK_EN | 0x00 << SYSCLK_SRC | 0x01 << SYSCLK_EN, ++ ac10x); ++ ac10x->mclk = ac10x->sysclk; ++ } ++ ++ return 0; ++} ++ ++/* ++ * support no more than 16 slots. ++ */ ++static int ac108_multi_chips_slots(struct ac10x_priv *ac, int slots) ++{ ++ int i; ++ ++ /* ++ * codec0 enable slots 2,3,0,1 when 1 codec ++ * ++ * codec0 enable slots 6,7,0,1 when 2 codec ++ * codec1 enable slots 2,3,4,5 ++ * ++ * ... ++ */ ++ for (i = 0; i < ac->codec_cnt; i++) { ++ /* rotate map, due to channels rotated by CPU_DAI */ ++ const unsigned vec_mask[] = { ++ 0x3 << 6 | 0x3, // slots 6,7,0,1 ++ 0xF << 2, // slots 2,3,4,5 ++ 0, ++ 0, ++ }; ++ const unsigned vec_maps[] = { ++ /* ++ * chip 0, ++ * mic 0 sample -> slot 6 ++ * mic 1 sample -> slot 7 ++ * mic 2 sample -> slot 0 ++ * mic 3 sample -> slot 1 ++ */ ++ 0x0 << 12 | 0x1 << 14 | 0x2 << 0 | 0x3 << 2, ++ /* ++ * chip 1, ++ * mic 0 sample -> slot 2 ++ * mic 1 sample -> slot 3 ++ * mic 2 sample -> slot 4 ++ * mic 3 sample -> slot 5 ++ */ ++ 0x0 << 4 | 0x1 << 6 | 0x2 << 8 | 0x3 << 10, ++ 0, ++ 0, ++ }; ++ unsigned vec; ++ ++ /* 0x38-0x3A I2S_TX1_CTRLx */ ++ if (ac->codec_cnt == 1) { ++ vec = 0xFUL; ++ } else { ++ vec = vec_mask[i]; ++ } ++ ac10x_write(I2S_TX1_CTRL1, slots - 1, ac->i2cmap[i]); ++ ac10x_write(I2S_TX1_CTRL2, (vec >> 0) & 0xFF, ac->i2cmap[i]); ++ ac10x_write(I2S_TX1_CTRL3, (vec >> 8) & 0xFF, ac->i2cmap[i]); ++ ++ /* 0x3C-0x3F I2S_TX1_CHMP_CTRLx */ ++ if (ac->codec_cnt == 1) { ++ vec = (0x2 << 0 | 0x3 << 2 | 0x0 << 4 | 0x1 << 6); ++ } else if (ac->codec_cnt == 2) { ++ vec = vec_maps[i]; ++ } ++ ++ ac10x_write(I2S_TX1_CHMP_CTRL1, (vec >> 0) & 0xFF, ac->i2cmap[i]); ++ ac10x_write(I2S_TX1_CHMP_CTRL2, (vec >> 8) & 0xFF, ac->i2cmap[i]); ++ ac10x_write(I2S_TX1_CHMP_CTRL3, (vec >> 16) & 0xFF, ac->i2cmap[i]); ++ ac10x_write(I2S_TX1_CHMP_CTRL4, (vec >> 24) & 0xFF, ac->i2cmap[i]); ++ } ++ return 0; ++} ++ ++static int ac108_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) ++{ ++ unsigned int i, channels, samp_res, rate; ++ struct snd_soc_codec *codec = dai->codec; ++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec); ++ unsigned bclkdiv; ++ int ret = 0; ++ u8 v; ++ ++ dev_dbg(dai->dev, "%s() stream=%s play:%d capt:%d +++\n", __func__, ++ snd_pcm_stream_str(substream), ++ dai->stream[SNDRV_PCM_STREAM_PLAYBACK].active, ++ dai->stream[SNDRV_PCM_STREAM_CAPTURE].active); ++ ++ if (ac10x->i2c101) { ++ ret = ac101_hw_params(substream, params, dai); ++ if (ret > 0) { ++ dev_dbg(dai->dev, "%s() L%d returned\n", __func__, __LINE__); ++ /* not configure hw_param twice */ ++ return 0; ++ } ++ } ++ ++ if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE && ++ dai->stream[SNDRV_PCM_STREAM_PLAYBACK].active) ++ || (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && ++ dai->stream[SNDRV_PCM_STREAM_CAPTURE].active)) { ++ /* not configure hw_param twice */ ++ /* return 0; */ ++ } ++ ++ channels = params_channels(params); ++ ++ /* Master mode, to clear cpu_dai fifos, output bclk without lrck */ ++ ac10x_read(I2S_CTRL, &v, ac10x->i2cmap[_MASTER_INDEX]); ++ if (v & (0x01 << BCLK_IOEN)) { ++ ac10x_update_bits(I2S_CTRL, 0x1 << LRCK_IOEN, ++ 0x0 << LRCK_IOEN, ac10x->i2cmap[_MASTER_INDEX]); ++ } ++ ++ switch (params_format(params)) { ++ case SNDRV_PCM_FORMAT_S8: ++ samp_res = 0; ++ break; ++ case SNDRV_PCM_FORMAT_S16_LE: ++ samp_res = 2; ++ break; ++ case SNDRV_PCM_FORMAT_S20_3LE: ++ samp_res = 3; ++ break; ++ case SNDRV_PCM_FORMAT_S24_LE: ++ samp_res = 4; ++ break; ++ case SNDRV_PCM_FORMAT_S32_LE: ++ samp_res = 6; ++ break; ++ default: ++ dev_err(dai->dev, "AC108 don't supported the sample resolution: %u\n", ++ params_format(params)); ++ return -EINVAL; ++ } ++ ++ for (i = 0; i < ARRAY_SIZE(ac108_sample_rate); i++) { ++ if (ac108_sample_rate[i].real_val == params_rate(params) / ++ (ac10x->data_protocol + 1UL)) { ++ rate = i; ++ break; ++ } ++ } ++ if (i >= ARRAY_SIZE(ac108_sample_rate)) { ++ return -EINVAL; ++ } ++ ++ if (channels == 8 && ac108_sample_rate[rate].real_val == 96000) { ++ /* 24.576M bit clock is not support by ac108 */ ++ return -EINVAL; ++ } ++ ++ dev_dbg(dai->dev, "rate: %d , channels: %d , samp_res: %d", ++ ac108_sample_rate[rate].real_val, ++ channels, ++ ac108_samp_res[samp_res].real_val); ++ ++ /** ++ * 0x33: ++ * The 8-Low bit of LRCK period value. It is used to program ++ * the number of BCLKs per channel of sample frame. This value ++ * is interpreted as follow: ++ * The 8-Low bit of LRCK period value. It is used to program ++ * the number of BCLKs per channel of sample frame. This value ++ * is interpreted as follow: PCM mode: Number of BCLKs within ++ * (Left + Right) channel width I2S / Left-Justified / ++ * Right-Justified mode: Number of BCLKs within each individual ++ * channel width (Left or Right) N+1 ++ * For example: ++ * n = 7: 8 BCLK width ++ * … ++ * n = 1023: 1024 BCLKs width ++ * 0X32[0:1]: ++ * The 2-High bit of LRCK period value. ++ */ ++ if (ac10x->i2s_mode != PCM_FORMAT) { ++ if (ac10x->data_protocol) { ++ ac108_multi_write(I2S_LRCK_CTRL2, ac108_samp_res[samp_res].real_val - 1, ++ ac10x); ++ /*encoding mode, the max LRCK period value < 32,so the 2-High bit is zero*/ ++ ac108_multi_update_bits(I2S_LRCK_CTRL1, 0x03 << 0, 0x00, ac10x); ++ } else { ++ /*TDM mode or normal mode*/ ++ //ac108_multi_update_bits(I2S_LRCK_CTRL1, 0x03 << 0, 0x00, ac10x); ++ ac108_multi_write(I2S_LRCK_CTRL2, ac108_samp_res[samp_res].real_val - 1, ++ ac10x); ++ ac108_multi_update_bits(I2S_LRCK_CTRL1, 0x03 << 0, 0x00, ac10x); ++ } ++ } else { ++ unsigned div; ++ ++ /*TDM mode or normal mode*/ ++ div = ac108_samp_res[samp_res].real_val * channels - 1; ++ ac108_multi_write(I2S_LRCK_CTRL2, (div & 0xFF), ac10x); ++ ac108_multi_update_bits(I2S_LRCK_CTRL1, 0x03 << 0, (div >> 8) << 0, ac10x); ++ } ++ ++ /** ++ * 0x35: ++ * TX Encoding mode will add 4bits to mark channel number ++ * TODO: need a chat to explain this ++ */ ++ ac108_multi_update_bits(I2S_FMT_CTRL2, 0x07 << SAMPLE_RESOLUTION | 0x07 << SLOT_WIDTH_SEL, ++ ac108_samp_res[samp_res].reg_val << SAMPLE_RESOLUTION | ++ ac108_samp_res[samp_res].reg_val << SLOT_WIDTH_SEL, ac10x); ++ ++ /** ++ * 0x60: ++ * ADC Sample Rate synchronised with I2S1 clock zone ++ */ ++ ac108_multi_update_bits(ADC_SPRC, 0x0f << ADC_FS_I2S1, ++ ac108_sample_rate[rate].reg_val << ADC_FS_I2S1, ac10x); ++ ac108_multi_write(HPF_EN, 0x0F, ac10x); ++ ++ if (ac10x->i2c101 && _MASTER_MULTI_CODEC == _MASTER_AC101) { ++ ac108_config_pll(ac10x, ac108_sample_rate[rate].real_val, ++ ac108_samp_res[samp_res].real_val * channels); ++ } else { ++ ac108_config_pll(ac10x, ac108_sample_rate[rate].real_val, 0); ++ } ++ ++ /* ++ * master mode only ++ */ ++ bclkdiv = ac10x->mclk / (ac108_sample_rate[rate].real_val * channels * ++ ac108_samp_res[samp_res].real_val); ++ for (i = 0; i < ARRAY_SIZE(ac108_bclkdivs) - 1; i++) { ++ if (ac108_bclkdivs[i] >= bclkdiv) { ++ break; ++ } ++ } ++ ac108_multi_update_bits(I2S_BCLK_CTRL, 0x0F << BCLKDIV, i << BCLKDIV, ac10x); ++ ++ /* ++ * slots allocation for each chip ++ */ ++ ac108_multi_chips_slots(ac10x, channels); ++ ++ /*0x21: Module clock enable*/ ++ ac108_multi_write(MOD_CLK_EN, 1 << I2S | 1 << ADC_DIGITAL | ++ 1 << MIC_OFFSET_CALIBRATION | 1 << ADC_ANALOG, ac10x); ++ /*0x22: Module reset de-asserted*/ ++ ac108_multi_write(MOD_RST_CTRL, 1 << I2S | 1 << ADC_DIGITAL | ++ 1 << MIC_OFFSET_CALIBRATION | 1 << ADC_ANALOG, ac10x); ++ ++ ac108_multi_write(I2S_TX1_CHMP_CTRL1, 0xE4, ac10x); ++ ac108_multi_write(I2S_TX1_CHMP_CTRL2, 0xE4, ac10x); ++ ac108_multi_write(I2S_TX1_CHMP_CTRL3, 0xE4, ac10x); ++ ac108_multi_write(I2S_TX1_CHMP_CTRL4, 0xE4, ac10x); ++ ++ ac108_multi_write(I2S_TX2_CHMP_CTRL1, 0xE4, ac10x); ++ ac108_multi_write(I2S_TX2_CHMP_CTRL2, 0xE4, ac10x); ++ ac108_multi_write(I2S_TX2_CHMP_CTRL3, 0xE4, ac10x); ++ ac108_multi_write(I2S_TX2_CHMP_CTRL4, 0xE4, ac10x); ++ ++ dev_dbg(dai->dev, "%s() stream=%s ---\n", __func__, ++ snd_pcm_stream_str(substream)); ++ ++ return 0; ++} ++ ++static int ac108_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir) ++{ ++ ++ struct ac10x_priv *ac10x = snd_soc_dai_get_drvdata(dai); ++ ++ freq = 24000000; ++ clk_id = SYSCLK_SRC_PLL; ++ ++ switch (clk_id) { ++ case SYSCLK_SRC_MCLK: ++ ac108_multi_update_bits(SYSCLK_CTRL, 0x1 << SYSCLK_SRC, ++ SYSCLK_SRC_MCLK << SYSCLK_SRC, ac10x); ++ break; ++ case SYSCLK_SRC_PLL: ++ ac108_multi_update_bits(SYSCLK_CTRL, 0x1 << SYSCLK_SRC, ++ SYSCLK_SRC_PLL << SYSCLK_SRC, ac10x); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ac10x->sysclk = freq; ++ ac10x->clk_id = clk_id; ++ ++ return 0; ++} ++ ++/** ++ * The i2s format management related registers are Reg ++ * 30h~Reg36h ++ * 33h,35h will be set in ac108_hw_params, It's BCLK width and ++ * Sample Resolution. ++ * @author baozhu (17-6-20) ++ * ++ * @param dai ++ * @param fmt ++ * ++ * @return int ++ */ ++static int ac108_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) ++{ ++ unsigned char tx_offset, lrck_polarity, brck_polarity; ++ struct ac10x_priv *ac10x = dev_get_drvdata(dai->dev); ++ ++ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { ++ case SND_SOC_DAIFMT_CBM_CFM: /* AC108 Master */ ++ if (! ac10x->i2c101 || _MASTER_MULTI_CODEC == _MASTER_AC108) { ++ /** ++ * 0x30:chip is master mode ,BCLK & LRCK output ++ */ ++ ac108_multi_update_bits(I2S_CTRL, ++ 0x03 << LRCK_IOEN | 0x03 << SDO1_EN | ++ 0x1 << TXEN | 0x1 << GEN, ++ 0x03 << LRCK_IOEN | 0x01 << SDO1_EN | ++ 0x1 << TXEN | 0x1 << GEN, ac10x); ++ /* multi_chips: only one chip set as Master, and the others also need to set as Slave */ ++ ac10x_update_bits(I2S_CTRL, 0x3 << LRCK_IOEN, 0x01 << BCLK_IOEN, ++ ac10x->i2cmap[_MASTER_INDEX]); ++ } else { ++ /* TODO: Both cpu_dai and codec_dai(AC108) be set as slave in DTS */ ++ dev_err(dai->dev, "used as slave when AC101 is master\n"); ++ } ++ break; ++ case SND_SOC_DAIFMT_CBS_CFS: /* AC108 Slave */ ++ /** ++ * 0x30:chip is slave mode, BCLK & LRCK input,enable SDO1_EN and ++ * SDO2_EN, Transmitter Block Enable, Globe Enable ++ */ ++ ac108_multi_update_bits(I2S_CTRL, ++ 0x03 << LRCK_IOEN | 0x03 << SDO1_EN | ++ 0x1 << TXEN | 0x1 << GEN, ++ 0x00 << LRCK_IOEN | 0x03 << SDO1_EN | ++ 0x0 << TXEN | 0x0 << GEN, ac10x); ++ break; ++ default: ++ dev_err(dai->dev, "AC108 Master/Slave mode config error:%u\n\n", ++ (fmt & SND_SOC_DAIFMT_MASTER_MASK) >> 12); ++ return -EINVAL; ++ } ++ ++ /*AC108 config I2S/LJ/RJ/PCM format*/ ++ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { ++ case SND_SOC_DAIFMT_I2S: ++ ac10x->i2s_mode = LEFT_JUSTIFIED_FORMAT; ++ tx_offset = 1; ++ break; ++ case SND_SOC_DAIFMT_RIGHT_J: ++ ac10x->i2s_mode = RIGHT_JUSTIFIED_FORMAT; ++ tx_offset = 0; ++ break; ++ case SND_SOC_DAIFMT_LEFT_J: ++ ac10x->i2s_mode = LEFT_JUSTIFIED_FORMAT; ++ tx_offset = 0; ++ break; ++ case SND_SOC_DAIFMT_DSP_A: ++ ac10x->i2s_mode = PCM_FORMAT; ++ tx_offset = 1; ++ break; ++ case SND_SOC_DAIFMT_DSP_B: ++ ac10x->i2s_mode = PCM_FORMAT; ++ tx_offset = 0; ++ break; ++ default: ++ dev_err(dai->dev, "AC108 I2S format config error:%u\n\n", ++ fmt & SND_SOC_DAIFMT_FORMAT_MASK); ++ return -EINVAL; ++ } ++ ++ /*AC108 config BCLK&LRCK polarity*/ ++ switch (fmt & SND_SOC_DAIFMT_INV_MASK) { ++ case SND_SOC_DAIFMT_NB_NF: ++ brck_polarity = BCLK_NORMAL_DRIVE_N_SAMPLE_P; ++ lrck_polarity = LRCK_LEFT_HIGH_RIGHT_LOW; ++ break; ++ case SND_SOC_DAIFMT_NB_IF: ++ brck_polarity = BCLK_NORMAL_DRIVE_N_SAMPLE_P; ++ lrck_polarity = LRCK_LEFT_LOW_RIGHT_HIGH; ++ break; ++ case SND_SOC_DAIFMT_IB_NF: ++ brck_polarity = BCLK_INVERT_DRIVE_P_SAMPLE_N; ++ lrck_polarity = LRCK_LEFT_HIGH_RIGHT_LOW; ++ break; ++ case SND_SOC_DAIFMT_IB_IF: ++ brck_polarity = BCLK_INVERT_DRIVE_P_SAMPLE_N; ++ lrck_polarity = LRCK_LEFT_LOW_RIGHT_HIGH; ++ break; ++ default: ++ dev_err(dai->dev, "AC108 config BCLK/LRCLK polarity error:%u\n\n", ++ (fmt & SND_SOC_DAIFMT_INV_MASK) >> 8); ++ return -EINVAL; ++ } ++ ++ ac108_configure_power(ac10x); ++ ++ /** ++ *0x31: 0: normal mode, negative edge drive and positive edge sample ++ 1: invert mode, positive edge drive and negative edge sample ++ */ ++ ac108_multi_update_bits(I2S_BCLK_CTRL, 0x01 << BCLK_POLARITY, ++ brck_polarity << BCLK_POLARITY, ac10x); ++ /** ++ * 0x32: same as 0x31 ++ */ ++ ac108_multi_update_bits(I2S_LRCK_CTRL1, 0x01 << LRCK_POLARITY, ++ lrck_polarity << LRCK_POLARITY, ac10x); ++ /** ++ * 0x34:Encoding Mode Selection,Mode ++ * Selection,data is offset by 1 BCLKs to LRCK ++ * normal mode for the last half cycle of BCLK in the slot ? ++ * turn to hi-z state (TDM) when not transferring slot ? ++ */ ++ ac108_multi_update_bits(I2S_FMT_CTRL1, ++ 0x01 << ENCD_SEL | 0x03 << MODE_SEL | 0x01 << TX2_OFFSET | ++ 0x01 << TX1_OFFSET | 0x01 << TX_SLOT_HIZ | 0x01 << TX_STATE, ++ ac10x->data_protocol << ENCD_SEL | ac10x->i2s_mode << MODE_SEL | ++ tx_offset << TX2_OFFSET | tx_offset << TX1_OFFSET | ++ 0x00 << TX_SLOT_HIZ | 0x01 << TX_STATE, ac10x); ++ ++ /** ++ * 0x60: ++ * MSB / LSB First Select: This driver only support MSB First Select . ++ * OUT2_MUTE,OUT1_MUTE shoule be set in widget. ++ * LRCK = 1 BCLK width ++ * Linear PCM ++ * ++ * TODO:pcm mode, bit[0:1] and bit[2] is special ++ */ ++ ac108_multi_update_bits(I2S_FMT_CTRL3, ++ 0x01 << TX_MLS | 0x03 << SEXT | ++ 0x01 << LRCK_WIDTH | 0x03 << TX_PDM, ++ 0x00 << TX_MLS | 0x03 << SEXT | ++ 0x00 << LRCK_WIDTH | 0x00 << TX_PDM, ac10x); ++ ++ ac108_multi_write(HPF_EN, 0x00, ac10x); ++ ++ if (ac10x->i2c101) { ++ return ac101_set_dai_fmt(dai, fmt); ++ } ++ return 0; ++} ++ ++/* ++ * due to miss channels order in cpu_dai, we meed defer the clock starting. ++ */ ++static int ac108_set_clock(int y_start_n_stop) ++{ ++ u8 reg; ++ int ret = 0; ++ ++ dev_dbg(ac10x->codec->dev, "%s() L%d cmd:%d\n", __func__, __LINE__, y_start_n_stop); ++ ++ /* spin_lock move to machine trigger */ ++ ++ if (y_start_n_stop && ac10x->sysclk_en == 0) { ++ /* enable lrck clock */ ++ ac10x_read(I2S_CTRL, ®, ac10x->i2cmap[_MASTER_INDEX]); ++ if (reg & (0x01 << BCLK_IOEN)) { ++ ret = ret || ac10x_update_bits(I2S_CTRL, 0x03 << LRCK_IOEN, ++ 0x03 << LRCK_IOEN, ++ ac10x->i2cmap[_MASTER_INDEX]); ++ } ++ ++ /*0x10: PLL Common voltage enable, PLL enable */ ++ ret = ret || ac108_multi_update_bits(PLL_CTRL1, ++ 0x01 << PLL_EN | 0x01 << PLL_COM_EN, ++ 0x01 << PLL_EN | 0x01 << PLL_COM_EN, ac10x); ++ /* enable global clock */ ++ ret = ret || ac108_multi_update_bits(I2S_CTRL, 0x1 << TXEN | 0x1 << GEN, ++ 0x1 << TXEN | 0x1 << GEN, ac10x); ++ ++ ac10x->sysclk_en = 1UL; ++ } else if (!y_start_n_stop && ac10x->sysclk_en != 0) { ++ /* disable global clock */ ++ ret = ret || ac108_multi_update_bits(I2S_CTRL, 0x1 << TXEN | 0x1 << GEN, ++ 0x0 << TXEN | 0x0 << GEN, ac10x); ++ ++ /*0x10: PLL Common voltage disable, PLL disable */ ++ ret = ret || ac108_multi_update_bits(PLL_CTRL1, 0x01 << PLL_EN | 0x01 << PLL_COM_EN, ++ 0x00 << PLL_EN | 0x00 << PLL_COM_EN, ac10x); ++ ++ /* disable lrck clock if it's enabled */ ++ ac10x_read(I2S_CTRL, ®, ac10x->i2cmap[_MASTER_INDEX]); ++ if (reg & (0x01 << LRCK_IOEN)) { ++ ret = ret || ac10x_update_bits(I2S_CTRL, 0x03 << LRCK_IOEN, ++ 0x01 << BCLK_IOEN, ++ ac10x->i2cmap[_MASTER_INDEX]); ++ } ++ if (!ret) { ++ ac10x->sysclk_en = 0UL; ++ } ++ } ++ ++ return ret; ++} ++ ++static int ac108_prepare(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ u8 r; ++ ++ dev_dbg(dai->dev, "%s() stream=%s\n", ++ __func__, ++ snd_pcm_stream_str(substream)); ++ ++ if (ac10x->i2c101 && _MASTER_MULTI_CODEC == _MASTER_AC101) { ++ ac101_trigger(substream, SNDRV_PCM_TRIGGER_START, dai); ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { ++ return 0; ++ } ++ } ++ ++ ac10x_read(I2S_CTRL, &r, ac10x->i2cmap[_MASTER_INDEX]); ++ if ((r & (0x01 << BCLK_IOEN)) && (r & (0x01 << LRCK_IOEN)) == 0) { ++ /* disable global clock */ ++ ac108_multi_update_bits(I2S_CTRL, 0x1 << TXEN | 0x1 << GEN, ++ 0x0 << TXEN | 0x0 << GEN, ac10x); ++ } ++ ++ /* delayed clock starting, move to machine trigger() */ ++ ac108_set_clock(1); ++ ++ return 0; ++} ++ ++int ac108_audio_startup(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ struct snd_soc_codec *codec = dai->codec; ++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec); ++ ++ if (ac10x->i2c101) { ++ return ac101_audio_startup(substream, dai); ++ } ++ return 0; ++} ++ ++void ac108_aif_shutdown(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ struct snd_soc_codec *codec = dai->codec; ++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec); ++ ++ ac108_set_clock(0); ++ ++ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { ++ /*0x21: Module clock disable */ ++ ac108_multi_write(MOD_CLK_EN, 0x0, ac10x); ++ /*0x22: Module reset asserted */ ++ ac108_multi_write(MOD_RST_CTRL, 0x0, ac10x); ++ } ++ ++ if (ac10x->i2c101) { ++ ac101_aif_shutdown(substream, dai); ++ } ++} ++ ++int ac108_aif_mute(struct snd_soc_dai *dai, int mute, int stream) ++{ ++ struct snd_soc_codec *codec = dai->codec; ++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec); ++ ++ if (ac10x->i2c101) { ++ return ac101_aif_mute(dai, mute); ++ } ++ return 0; ++} ++ ++static const struct snd_soc_dai_ops ac108_dai_ops = { ++ .startup = ac108_audio_startup, ++ .shutdown = ac108_aif_shutdown, ++ ++ /*DAI clocking configuration*/ ++ .set_sysclk = ac108_set_sysclk, ++ ++ /*ALSA PCM audio operations*/ ++ .hw_params = ac108_hw_params, ++ .prepare = ac108_prepare, ++ .mute_stream = ac108_aif_mute, ++ ++ /*DAI format configuration*/ ++ .set_fmt = ac108_set_fmt, ++}; ++ ++static struct snd_soc_dai_driver ac108_dai0 = { ++ .name = "ac10x-codec0", ++ #if _USE_CAPTURE ++ .playback = { ++ .stream_name = "Playback", ++ .channels_min = 1, ++ .channels_max = AC108_CHANNELS_MAX, ++ .rates = AC108_RATES, ++ .formats = AC108_FORMATS, ++ }, ++ #endif ++ .capture = { ++ .stream_name = "Capture", ++ .channels_min = 1, ++ .channels_max = AC108_CHANNELS_MAX, ++ .rates = AC108_RATES, ++ .formats = AC108_FORMATS, ++ }, ++ .ops = &ac108_dai_ops, ++}; ++ ++static struct snd_soc_dai_driver ac108_dai1 = { ++ .name = "ac10x-codec1", ++ #if _USE_CAPTURE ++ .playback = { ++ .stream_name = "Playback", ++ .channels_min = 1, ++ .channels_max = AC108_CHANNELS_MAX, ++ .rates = AC108_RATES, ++ .formats = AC108_FORMATS, ++ }, ++ #endif ++ .capture = { ++ .stream_name = "Capture", ++ .channels_min = 1, ++ .channels_max = AC108_CHANNELS_MAX, ++ .rates = AC108_RATES, ++ .formats = AC108_FORMATS, ++ }, ++ .ops = &ac108_dai_ops, ++}; ++ ++static struct snd_soc_dai_driver *ac108_dai[] = { ++ &ac108_dai0, ++ &ac108_dai1, ++}; ++ ++static int ac108_add_widgets(struct snd_soc_codec *codec) ++{ ++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec); ++ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); ++ const struct snd_kcontrol_new* snd_kcntl = ac108_snd_controls; ++ int ctrl_cnt = ARRAY_SIZE(ac108_snd_controls); ++ ++ /* only register controls correspond to exist chips */ ++ if (ac10x->tdm_chips_cnt >= 2) { ++ snd_kcntl = ac108tdm_snd_controls; ++ ctrl_cnt = ARRAY_SIZE(ac108tdm_snd_controls); ++ } ++ snd_soc_add_codec_controls(codec, snd_kcntl, ctrl_cnt); ++ ++ snd_soc_dapm_new_controls(dapm, ac108_dapm_widgets,ARRAY_SIZE(ac108_dapm_widgets)); ++ snd_soc_dapm_add_routes(dapm, ac108_dapm_routes, ARRAY_SIZE(ac108_dapm_routes)); ++ ++ return 0; ++} ++ ++static int ac108_codec_probe(struct snd_soc_codec *codec) ++{ ++ spin_lock_init(&ac10x->lock); ++ ++ ac10x->codec = codec; ++ dev_set_drvdata(codec->dev, ac10x); ++ ac108_add_widgets(codec); ++ ++ if (ac10x->i2c101) { ++ ac101_codec_probe(codec); ++ } ++ ++ /* change default volume */ ++ ac108_multi_update_bits(ADC1_DVOL_CTRL, 0xff, 0xc8, ac10x); ++ ac108_multi_update_bits(ADC2_DVOL_CTRL, 0xff, 0xc8, ac10x); ++ ac108_multi_update_bits(ADC3_DVOL_CTRL, 0xff, 0xc8, ac10x); ++ ac108_multi_update_bits(ADC4_DVOL_CTRL, 0xff, 0xc8, ac10x); ++ ++ return 0; ++} ++ ++static int ac108_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) ++{ ++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec); ++ ++ dev_dbg(codec->dev, "AC108 level:%d\n", level); ++ ++ switch (level) { ++ case SND_SOC_BIAS_ON: ++ ac108_multi_update_bits(ANA_ADC1_CTRL1, 0x01 << ADC1_MICBIAS_EN, ++ 0x01 << ADC1_MICBIAS_EN, ac10x); ++ ac108_multi_update_bits(ANA_ADC2_CTRL1, 0x01 << ADC2_MICBIAS_EN, ++ 0x01 << ADC2_MICBIAS_EN, ac10x); ++ ac108_multi_update_bits(ANA_ADC3_CTRL1, 0x01 << ADC3_MICBIAS_EN, ++ 0x01 << ADC3_MICBIAS_EN, ac10x); ++ ac108_multi_update_bits(ANA_ADC4_CTRL1, 0x01 << ADC4_MICBIAS_EN, ++ 0x01 << ADC4_MICBIAS_EN, ac10x); ++ break; ++ ++ case SND_SOC_BIAS_PREPARE: ++ /* Put the MICBIASes into regulating mode */ ++ break; ++ ++ case SND_SOC_BIAS_STANDBY: ++ break; ++ ++ case SND_SOC_BIAS_OFF: ++ ac108_multi_update_bits(ANA_ADC1_CTRL1, 0x01 << ADC1_MICBIAS_EN, ++ 0x00 << ADC1_MICBIAS_EN, ac10x); ++ ac108_multi_update_bits(ANA_ADC2_CTRL1, 0x01 << ADC2_MICBIAS_EN, ++ 0x00 << ADC2_MICBIAS_EN, ac10x); ++ ac108_multi_update_bits(ANA_ADC3_CTRL1, 0x01 << ADC3_MICBIAS_EN, ++ 0x00 << ADC3_MICBIAS_EN, ac10x); ++ ac108_multi_update_bits(ANA_ADC4_CTRL1, 0x01 << ADC4_MICBIAS_EN, ++ 0x00 << ADC4_MICBIAS_EN, ac10x); ++ break; ++ } ++ ++ if (ac10x->i2c101) { ++ ac101_set_bias_level(codec, level); ++ } ++ return 0; ++} ++ ++int ac108_codec_remove(struct snd_soc_codec *codec) { ++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec); ++ ++ if (! ac10x->i2c101) { ++ return 0; ++ } ++ return ac101_codec_remove(codec); ++} ++#if __NO_SND_SOC_CODEC_DRV ++void ac108_codec_remove_void(struct snd_soc_codec *codec) ++{ ++ ac108_codec_remove(codec); ++} ++#define ac108_codec_remove ac108_codec_remove_void ++#endif ++ ++int ac108_codec_suspend(struct snd_soc_codec *codec) ++{ ++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec); ++ int i; ++ ++ for (i = 0; i < ac10x->codec_cnt; i++) { ++ regcache_cache_only(ac10x->i2cmap[i], true); ++ } ++ ++ if (! ac10x->i2c101) { ++ return 0; ++ } ++ return ac101_codec_suspend(codec); ++} ++ ++int ac108_codec_resume(struct snd_soc_codec *codec) ++{ ++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec); ++ int i, ret; ++ ++ /* Sync reg_cache with the hardware */ ++ for (i = 0; i < ac10x->codec_cnt; i++) { ++ regcache_cache_only(ac10x->i2cmap[i], false); ++ ret = regcache_sync(ac10x->i2cmap[i]); ++ if (ret != 0) { ++ dev_err(codec->dev, "Failed to sync i2cmap%d register cache: %d\n", ++ i, ret); ++ regcache_cache_only(ac10x->i2cmap[i], true); ++ } ++ } ++ ++ if (! ac10x->i2c101) { ++ return 0; ++ } ++ return ac101_codec_resume(codec); ++} ++ ++static struct snd_soc_codec_driver ac10x_soc_codec_driver = { ++ .probe = ac108_codec_probe, ++ .remove = ac108_codec_remove, ++ .suspend = ac108_codec_suspend, ++ .resume = ac108_codec_resume, ++ .set_bias_level = ac108_set_bias_level, ++ .read = ac108_codec_read, ++ .write = ac108_codec_write, ++}; ++ ++static ssize_t ac108_store(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ int val = 0, flag = 0; ++ u8 i = 0, reg, num, value_w, value_r[4]; ++ ++ val = simple_strtol(buf, NULL, 16); ++ flag = (val >> 16) & 0xF; ++ ++ if (flag) { ++ reg = (val >> 8) & 0xFF; ++ value_w = val & 0xFF; ++ ac108_multi_write(reg, value_w, ac10x); ++ dev_info(dev, "Write 0x%02x to REG:0x%02x\n", value_w, reg); ++ } else { ++ int k; ++ ++ reg = (val >> 8) & 0xFF; ++ num = val & 0xff; ++ dev_info(dev, "\nRead: start REG:0x%02x,count:0x%02x\n", reg, num); ++ ++ for (k = 0; k < ac10x->codec_cnt; k++) ++ regcache_cache_bypass(ac10x->i2cmap[k], true); ++ ++ do { ++ memset(value_r, 0, sizeof value_r); ++ ++ for (k = 0; k < ac10x->codec_cnt; k++) ++ ac10x_read(reg, &value_r[k], ac10x->i2cmap[k]); ++ ++ if (ac10x->codec_cnt >= 2) ++ dev_info(dev, "REG[0x%02x]: 0x%02x 0x%02x", reg, ++ value_r[0], value_r[1]); ++ else ++ dev_info(dev, "REG[0x%02x]: 0x%02x", reg, value_r[0]); ++ reg++; ++ ++ if ((++i == num) || (i % 4 == 0)) ++ dev_info(dev, "\n"); ++ } while (i < num); ++ ++ for (k = 0; k < ac10x->codec_cnt; k++) ++ regcache_cache_bypass(ac10x->i2cmap[k], false); ++ } ++ ++ return count; ++} ++ ++static ssize_t ac108_show(struct device *dev, struct device_attribute *attr, char *buf) ++{ ++ dev_info(dev, "echo flag|reg|val > ac108\n"); ++ dev_info(dev, "eg read star addres=0x06,count 0x10:echo 0610 >ac108\n"); ++ dev_info(dev, "eg write value:0xfe to address:0x06 :echo 106fe > ac108\n"); ++ return 0; ++} ++ ++static DEVICE_ATTR(ac108, 0644, ac108_show, ac108_store); ++static struct attribute *ac108_debug_attrs[] = { ++ &dev_attr_ac108.attr, ++ NULL, ++}; ++static struct attribute_group ac108_debug_attr_group = { ++ .name = "ac108_debug", ++ .attrs = ac108_debug_attrs, ++}; ++ ++static const struct i2c_device_id ac108_i2c_id[] = { ++ { "ac108_0", 0 }, ++ { "ac108_1", 1 }, ++ { "ac108_2", 2 }, ++ { "ac108_3", 3 }, ++ { "ac101", AC101_I2C_ID }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, ac108_i2c_id); ++ ++static const struct regmap_config ac108_regmap = { ++ .reg_bits = 8, ++ .val_bits = 8, ++ .reg_stride = 1, ++ .max_register = 0xDF, ++ .cache_type = REGCACHE_FLAT, ++}; ++static int ac108_i2c_probe(struct i2c_client *i2c) ++{ ++ struct device_node *np = i2c->dev.of_node; ++ const struct i2c_device_id *i2c_id; ++ unsigned int val = 0; ++ int ret = 0, index; ++ ++ if (ac10x == NULL) { ++ ac10x = kzalloc(sizeof(struct ac10x_priv), GFP_KERNEL); ++ if (ac10x == NULL) { ++ dev_err(&i2c->dev, "Unable to allocate ac10x private data\n"); ++ return -ENOMEM; ++ } ++ } ++ ++ i2c_id = i2c_match_id(ac108_i2c_id, i2c); ++ index = (int)i2c_id->driver_data; ++ if (index == AC101_I2C_ID) { ++ ac10x->i2c101 = i2c; ++ i2c_set_clientdata(i2c, ac10x); ++ ret = ac101_probe(i2c, i2c_id); ++ if (ret) { ++ ac10x->i2c101 = NULL; ++ return ret; ++ } ++ goto __ret; ++ } ++ ++ ret = of_property_read_u32(np, "data-protocol", &val); ++ if (ret) { ++ dev_err(&i2c->dev, "Please set data-protocol.\n"); ++ return -EINVAL; ++ } ++ ac10x->data_protocol = val; ++ ++ if (of_property_read_u32(np, "tdm-chips-count", &val)) val = 1; ++ ac10x->tdm_chips_cnt = val; ++ ++ dev_info(&i2c->dev, " ac10x i2c_id number: %d\n", index); ++ dev_info(&i2c->dev, " ac10x data protocol: %d\n", ac10x->data_protocol); ++ ++ ac10x->i2c[index] = i2c; ++ ac10x->i2cmap[index] = devm_regmap_init_i2c(i2c, &ac108_regmap); ++ if (IS_ERR(ac10x->i2cmap[index])) { ++ ret = PTR_ERR(ac10x->i2cmap[index]); ++ dev_err(&i2c->dev, "Fail to initialize i2cmap%d I/O: %d\n", index, ret); ++ return ret; ++ } ++ ++ /* ++ * Writing this register with 0x12 ++ * will resets all register to their default state. ++ */ ++ regcache_cache_only(ac10x->i2cmap[index], false); ++ ++ ret = regmap_write(ac10x->i2cmap[index], CHIP_RST, CHIP_RST_VAL); ++ msleep(1); ++ ++ /* sync regcache for FLAT type */ ++ ac10x_fill_regcache(&i2c->dev, ac10x->i2cmap[index]); ++ ++ ac10x->codec_cnt++; ++ dev_info(&i2c->dev, " ac10x codec count : %d\n", ac10x->codec_cnt); ++ ++ ret = sysfs_create_group(&i2c->dev.kobj, &ac108_debug_attr_group); ++ if (ret) { ++ dev_err(&i2c->dev, "failed to create attr group\n"); ++ } ++ ++__ret: ++ /* It's time to bind codec to i2c[_MASTER_INDEX] when all i2c are ready */ ++ if ((ac10x->codec_cnt != 0 && ac10x->tdm_chips_cnt < 2) ++ || (ac10x->i2c[0] && ac10x->i2c[1] && ac10x->i2c101)) { ++ /* no playback stream */ ++ if (! ac10x->i2c101) { ++ memset(&ac108_dai[_MASTER_INDEX]->playback, '\0', ++ sizeof ac108_dai[_MASTER_INDEX]->playback); ++ } ++ ret = snd_soc_register_codec(&ac10x->i2c[_MASTER_INDEX]->dev, ++ &ac10x_soc_codec_driver, ++ ac108_dai[_MASTER_INDEX], 1); ++ if (ret < 0) { ++ dev_err(&i2c->dev, "Failed to register ac10x codec: %d\n", ret); ++ } ++ } ++ return ret; ++} ++ ++static void ac108_i2c_remove(struct i2c_client *i2c) ++{ ++ if (ac10x->codec != NULL) { ++ snd_soc_unregister_codec(&ac10x->i2c[_MASTER_INDEX]->dev); ++ ac10x->codec = NULL; ++ } ++ if (i2c == ac10x->i2c101) { ++ ac101_remove(ac10x->i2c101); ++ ac10x->i2c101 = NULL; ++ goto __ret; ++ } ++ ++ if (i2c == ac10x->i2c[0]) { ++ ac10x->i2c[0] = NULL; ++ } ++ if (i2c == ac10x->i2c[1]) { ++ ac10x->i2c[1] = NULL; ++ } ++ ++ sysfs_remove_group(&i2c->dev.kobj, &ac108_debug_attr_group); ++ ++__ret: ++ if (!ac10x->i2c[0] && !ac10x->i2c[1] && !ac10x->i2c101) { ++ kfree(ac10x); ++ ac10x = NULL; ++ } ++} ++ ++static const struct of_device_id ac108_of_match[] = { ++ { .compatible = "x-power,ac108_0", }, ++ { .compatible = "x-power,ac108_1", }, ++ { .compatible = "x-power,ac108_2", }, ++ { .compatible = "x-power,ac108_3", }, ++ { .compatible = "x-power,ac101", }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, ac108_of_match); ++ ++static struct i2c_driver ac108_i2c_driver = { ++ .driver = { ++ .name = "ac10x-codec", ++ .of_match_table = ac108_of_match, ++ }, ++ .probe = ac108_i2c_probe, ++ .remove = ac108_i2c_remove, ++ .id_table = ac108_i2c_id, ++}; ++ ++module_i2c_driver(ac108_i2c_driver); ++ ++MODULE_DESCRIPTION("ASoC AC108 driver"); ++MODULE_AUTHOR("Baozhu Zuo"); ++MODULE_LICENSE("GPL"); +--- /dev/null ++++ b/sound/soc/codecs/ac108.h +@@ -0,0 +1,749 @@ ++/* ++ * ac108.h -- ac108 ALSA Soc Audio driver ++ * ++ * Author: panjunwen ++ * ++ * 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. ++ * ++ */ ++ ++#ifndef _AC108_H ++#define _AC108_H ++ ++/*** AC108 Codec Register Define***/ ++ ++//Chip Reset ++#define CHIP_RST 0x00 ++#define CHIP_RST_VAL 0x12 ++ ++//Power Control ++#define PWR_CTRL1 0x01 ++#define PWR_CTRL2 0x02 ++#define PWR_CTRL3 0x03 ++#define PWR_CTRL4 0x04 ++#define PWR_CTRL5 0x05 ++#define PWR_CTRL6 0x06 ++#define PWR_CTRL7 0x07 ++#define PWR_CTRL8 0x08 ++#define PWR_CTRL9 0x09 ++ ++//PLL Configure Control ++#define PLL_CTRL1 0x10 ++#define PLL_CTRL2 0x11 ++#define PLL_CTRL3 0x12 ++#define PLL_CTRL4 0x13 ++#define PLL_CTRL5 0x14 ++#define PLL_CTRL6 0x16 ++#define PLL_CTRL7 0x17 ++#define PLL_LOCK_CTRL 0x18 ++ ++//System Clock Control ++#define SYSCLK_CTRL 0x20 ++#define MOD_CLK_EN 0x21 ++#define MOD_RST_CTRL 0x22 ++#define DSM_CLK_CTRL 0x25 ++ ++//I2S Common Control ++#define I2S_CTRL 0x30 ++#define I2S_BCLK_CTRL 0x31 ++#define I2S_LRCK_CTRL1 0x32 ++#define I2S_LRCK_CTRL2 0x33 ++#define I2S_FMT_CTRL1 0x34 ++#define I2S_FMT_CTRL2 0x35 ++#define I2S_FMT_CTRL3 0x36 ++ ++//I2S TX1 Control ++#define I2S_TX1_CTRL1 0x38 ++#define I2S_TX1_CTRL2 0x39 ++#define I2S_TX1_CTRL3 0x3A ++#define I2S_TX1_CHMP_CTRL1 0x3C ++#define I2S_TX1_CHMP_CTRL2 0x3D ++#define I2S_TX1_CHMP_CTRL3 0x3E ++#define I2S_TX1_CHMP_CTRL4 0x3F ++ ++//I2S TX2 Control ++#define I2S_TX2_CTRL1 0x40 ++#define I2S_TX2_CTRL2 0x41 ++#define I2S_TX2_CTRL3 0x42 ++#define I2S_TX2_CHMP_CTRL1 0x44 ++#define I2S_TX2_CHMP_CTRL2 0x45 ++#define I2S_TX2_CHMP_CTRL3 0x46 ++#define I2S_TX2_CHMP_CTRL4 0x47 ++ ++//I2S RX1 Control ++#define I2S_RX1_CTRL1 0x50 ++#define I2S_RX1_CHMP_CTRL1 0x54 ++#define I2S_RX1_CHMP_CTRL2 0x55 ++#define I2S_RX1_CHMP_CTRL3 0x56 ++#define I2S_RX1_CHMP_CTRL4 0x57 ++ ++//I2S Loopback Debug ++#define I2S_LPB_DEBUG 0x58 ++ ++//ADC Common Control ++#define ADC_SPRC 0x60 ++#define ADC_DIG_EN 0x61 ++#define DMIC_EN 0x62 ++#define ADC_DSR 0x63 ++#define ADC_FIR 0x64 ++#define ADC_DDT_CTRL 0x65 ++ ++//HPF Control ++#define HPF_EN 0x66 ++#define HPF_COEF_REGH1 0x67 ++#define HPF_COEF_REGH2 0x68 ++#define HPF_COEF_REGL1 0x69 ++#define HPF_COEF_REGL2 0x6A ++#define HPF_GAIN_REGH1 0x6B ++#define HPF_GAIN_REGH2 0x6C ++#define HPF_GAIN_REGL1 0x6D ++#define HPF_GAIN_REGL2 0x6E ++ ++//ADC Digital Channel Volume Control ++#define ADC1_DVOL_CTRL 0x70 ++#define ADC2_DVOL_CTRL 0x71 ++#define ADC3_DVOL_CTRL 0x72 ++#define ADC4_DVOL_CTRL 0x73 ++ ++//ADC Digital Mixer Source and Gain Control ++#define ADC1_DMIX_SRC 0x76 ++#define ADC2_DMIX_SRC 0x77 ++#define ADC3_DMIX_SRC 0x78 ++#define ADC4_DMIX_SRC 0x79 ++ ++//ADC Digital Debug Control ++#define ADC_DIG_DEBUG 0x7F ++ ++//I2S Pad Drive Control ++#define I2S_DAT_PADDRV_CTRL 0x80 ++#define I2S_CLK_PADDRV_CTRL 0x81 ++ ++//Analog PGA Control ++#define ANA_PGA1_CTRL 0x90 ++#define ANA_PGA2_CTRL 0x91 ++#define ANA_PGA3_CTRL 0x92 ++#define ANA_PGA4_CTRL 0x93 ++ ++//MIC Offset Control ++#define MIC_OFFSET_CTRL1 0x96 ++#define MIC_OFFSET_CTRL2 0x97 ++#define MIC1_OFFSET_STATU1 0x98 ++#define MIC1_OFFSET_STATU2 0x99 ++#define MIC2_OFFSET_STATU1 0x9A ++#define MIC2_OFFSET_STATU2 0x9B ++#define MIC3_OFFSET_STATU1 0x9C ++#define MIC3_OFFSET_STATU2 0x9D ++#define MIC4_OFFSET_STATU1 0x9E ++#define MIC4_OFFSET_STATU2 0x9F ++ ++//ADC1 Analog Control ++#define ANA_ADC1_CTRL1 0xA0 ++#define ANA_ADC1_CTRL2 0xA1 ++#define ANA_ADC1_CTRL3 0xA2 ++#define ANA_ADC1_CTRL4 0xA3 ++#define ANA_ADC1_CTRL5 0xA4 ++#define ANA_ADC1_CTRL6 0xA5 ++#define ANA_ADC1_CTRL7 0xA6 ++ ++//ADC2 Analog Control ++#define ANA_ADC2_CTRL1 0xA7 ++#define ANA_ADC2_CTRL2 0xA8 ++#define ANA_ADC2_CTRL3 0xA9 ++#define ANA_ADC2_CTRL4 0xAA ++#define ANA_ADC2_CTRL5 0xAB ++#define ANA_ADC2_CTRL6 0xAC ++#define ANA_ADC2_CTRL7 0xAD ++ ++//ADC3 Analog Control ++#define ANA_ADC3_CTRL1 0xAE ++#define ANA_ADC3_CTRL2 0xAF ++#define ANA_ADC3_CTRL3 0xB0 ++#define ANA_ADC3_CTRL4 0xB1 ++#define ANA_ADC3_CTRL5 0xB2 ++#define ANA_ADC3_CTRL6 0xB3 ++#define ANA_ADC3_CTRL7 0xB4 ++ ++//ADC4 Analog Control ++#define ANA_ADC4_CTRL1 0xB5 ++#define ANA_ADC4_CTRL2 0xB6 ++#define ANA_ADC4_CTRL3 0xB7 ++#define ANA_ADC4_CTRL4 0xB8 ++#define ANA_ADC4_CTRL5 0xB9 ++#define ANA_ADC4_CTRL6 0xBA ++#define ANA_ADC4_CTRL7 0xBB ++ ++//GPIO Configure ++#define GPIO_CFG1 0xC0 ++#define GPIO_CFG2 0xC1 ++#define GPIO_DAT 0xC2 ++#define GPIO_DRV 0xC3 ++#define GPIO_PULL 0xC4 ++#define GPIO_INT_CFG 0xC5 ++#define GPIO_INT_EN 0xC6 ++#define GPIO_INT_STATUS 0xC7 ++ ++//Misc ++#define BGTC_DAT 0xD1 ++#define BGVC_DAT 0xD2 ++#define PRNG_CLK_CTRL 0xDF ++ ++/*** AC108 Codec Register Bit Define***/ ++ ++/*PWR_CTRL1*/ ++#define CP12_CTRL 4 ++#define CP12_SENSE_SELECT 3 ++ ++/*PWR_CTRL2*/ ++#define CP12_SENSE_FILT 6 ++#define CP12_COMP_FF_EN 3 ++#define CP12_FORCE_ENABLE 2 ++#define CP12_FORCE_RSTB 1 ++ ++/*PWR_CTRL3*/ ++#define LDO33DIG_CTRL 0 ++ ++/*PWR_CTRL6*/ ++#define LDO33ANA_2XHDRM 2 ++#define LDO33ANA_ENABLE 0 ++ ++/*PWR_CTRL7*/ ++#define VREF_SEL 3 ++#define VREF_FASTSTART_ENABLE 1 ++#define VREF_ENABLE 0 ++ ++/*PWR_CTRL9*/ ++#define VREFP_FASTSTART_ENABLE 7 ++#define VREFP_RESCTRL 5 ++#define VREFP_LPMODE 4 ++#define IGEN_TRIM 1 ++#define VREFP_ENABLE 0 ++ ++/*PLL_CTRL1*/ ++#define PLL_IBIAS 4 ++#define PLL_NDET 3 ++#define PLL_LOCKED_STATUS 2 ++#define PLL_COM_EN 1 ++#define PLL_EN 0 ++ ++/*PLL_CTRL2*/ ++#define PLL_PREDIV2 5 ++#define PLL_PREDIV1 0 ++ ++/*PLL_CTRL3*/ ++#define PLL_LOOPDIV_MSB 0 ++ ++/*PLL_CTRL4*/ ++#define PLL_LOOPDIV_LSB 0 ++ ++/*PLL_CTRL5*/ ++#define PLL_POSTDIV2 5 ++#define PLL_POSTDIV1 0 ++ ++/*PLL_CTRL6*/ ++#define PLL_LDO 6 ++#define PLL_CP 0 ++ ++/*PLL_CTRL7*/ ++#define PLL_CAP 6 ++#define PLL_RES 4 ++#define PLL_TEST_EN 0 ++ ++/*PLL_LOCK_CTRL*/ ++#define LOCK_LEVEL1 2 ++#define LOCK_LEVEL2 1 ++#define PLL_LOCK_EN 0 ++ ++/*SYSCLK_CTRL*/ ++#define PLLCLK_EN 7 ++#define PLLCLK_SRC 4 ++#define SYSCLK_SRC 3 ++#define SYSCLK_EN 0 ++ ++/*MOD_CLK_EN & MOD_RST_CTRL*/ ++#define I2S 7 ++#define ADC_DIGITAL 4 ++#define MIC_OFFSET_CALIBRATION 1 ++#define ADC_ANALOG 0 ++ ++/*DSM_CLK_CTRL*/ ++#define MIC_OFFSET_DIV 4 ++#define DSM_CLK_SEL 0 ++ ++/*I2S_CTRL*/ ++#define BCLK_IOEN 7 ++#define LRCK_IOEN 6 ++#define SDO2_EN 5 ++#define SDO1_EN 4 ++#define TXEN 2 ++#define RXEN 1 ++#define GEN 0 ++ ++/*I2S_BCLK_CTRL*/ ++#define EDGE_TRANSFER 5 ++#define BCLK_POLARITY 4 ++#define BCLKDIV 0 ++ ++/*I2S_LRCK_CTRL1*/ ++#define LRCK_POLARITY 4 ++#define LRCK_PERIODH 0 ++ ++/*I2S_LRCK_CTRL2*/ ++#define LRCK_PERIODL 0 ++ ++/*I2S_FMT_CTRL1*/ ++#define ENCD_SEL 6 ++#define MODE_SEL 4 ++#define TX2_OFFSET 3 ++#define TX1_OFFSET 2 ++#define TX_SLOT_HIZ 1 ++#define TX_STATE 0 ++ ++/*I2S_FMT_CTRL2*/ ++#define SLOT_WIDTH_SEL 4 ++#define SAMPLE_RESOLUTION 0 ++ ++/*I2S_FMT_CTRL3*/ ++#define TX_MLS 7 ++#define SEXT 5 ++#define OUT2_MUTE 4 ++#define OUT1_MUTE 3 ++#define LRCK_WIDTH 2 ++#define TX_PDM 0 ++ ++/*I2S_TX1_CTRL1*/ ++#define TX1_CHSEL 0 ++ ++/*I2S_TX1_CTRL2*/ ++#define TX1_CH8_EN 7 ++#define TX1_CH7_EN 6 ++#define TX1_CH6_EN 5 ++#define TX1_CH5_EN 4 ++#define TX1_CH4_EN 3 ++#define TX1_CH3_EN 2 ++#define TX1_CH2_EN 1 ++#define TX1_CH1_EN 0 ++ ++/*I2S_TX1_CTRL3*/ ++#define TX1_CH16_EN 7 ++#define TX1_CH15_EN 6 ++#define TX1_CH14_EN 5 ++#define TX1_CH13_EN 4 ++#define TX1_CH12_EN 3 ++#define TX1_CH11_EN 2 ++#define TX1_CH10_EN 1 ++#define TX1_CH9_EN 0 ++ ++/*I2S_TX1_CHMP_CTRL1*/ ++#define TX1_CH4_MAP 6 ++#define TX1_CH3_MAP 4 ++#define TX1_CH2_MAP 2 ++#define TX1_CH1_MAP 0 ++ ++/*I2S_TX1_CHMP_CTRL2*/ ++#define TX1_CH8_MAP 6 ++#define TX1_CH7_MAP 4 ++#define TX1_CH6_MAP 2 ++#define TX1_CH5_MAP 0 ++ ++/*I2S_TX1_CHMP_CTRL3*/ ++#define TX1_CH12_MAP 6 ++#define TX1_CH11_MAP 4 ++#define TX1_CH10_MAP 2 ++#define TX1_CH9_MAP 0 ++ ++/*I2S_TX1_CHMP_CTRL4*/ ++#define TX1_CH16_MAP 6 ++#define TX1_CH15_MAP 4 ++#define TX1_CH14_MAP 2 ++#define TX1_CH13_MAP 0 ++ ++/*I2S_TX2_CTRL1*/ ++#define TX2_CHSEL 0 ++ ++/*I2S_TX2_CHMP_CTRL1*/ ++#define TX2_CH4_MAP 6 ++#define TX2_CH3_MAP 4 ++#define TX2_CH2_MAP 2 ++#define TX2_CH1_MAP 0 ++ ++/*I2S_TX2_CHMP_CTRL2*/ ++#define TX2_CH8_MAP 6 ++#define TX2_CH7_MAP 4 ++#define TX2_CH6_MAP 2 ++#define TX2_CH5_MAP 0 ++ ++/*I2S_TX2_CHMP_CTRL3*/ ++#define TX2_CH12_MAP 6 ++#define TX2_CH11_MAP 4 ++#define TX2_CH10_MAP 2 ++#define TX2_CH9_MAP 0 ++ ++/*I2S_TX2_CHMP_CTRL4*/ ++#define TX2_CH16_MAP 6 ++#define TX2_CH15_MAP 4 ++#define TX2_CH14_MAP 2 ++#define TX2_CH13_MAP 0 ++ ++/*I2S_RX1_CTRL1*/ ++#define RX1_CHSEL 0 ++ ++/*I2S_RX1_CHMP_CTRL1*/ ++#define RX1_CH4_MAP 6 ++#define RX1_CH3_MAP 4 ++#define RX1_CH2_MAP 2 ++#define RX1_CH1_MAP 0 ++ ++/*I2S_RX1_CHMP_CTRL2*/ ++#define RX1_CH8_MAP 6 ++#define RX1_CH7_MAP 4 ++#define RX1_CH6_MAP 2 ++#define RX1_CH5_MAP 0 ++ ++/*I2S_RX1_CHMP_CTRL3*/ ++#define RX1_CH12_MAP 6 ++#define RX1_CH11_MAP 4 ++#define RX1_CH10_MAP 2 ++#define RX1_CH9_MAP 0 ++ ++/*I2S_RX1_CHMP_CTRL4*/ ++#define RX1_CH16_MAP 6 ++#define RX1_CH15_MAP 4 ++#define RX1_CH14_MAP 2 ++#define RX1_CH13_MAP 0 ++ ++/*I2S_LPB_DEBUG*/ ++#define I2S_LPB_DEBUG_EN 0 ++ ++/*ADC_SPRC*/ ++#define ADC_FS_I2S1 0 ++ ++/*ADC_DIG_EN*/ ++#define DG_EN 4 ++#define ENAD4 3 ++#define ENAD3 2 ++#define ENAD2 1 ++#define ENAD1 0 ++ ++/*DMIC_EN*/ ++#define DMIC2_EN 1 ++#define DMIC1_EN 0 ++ ++/*ADC_DSR*/ ++#define DIG_ADC4_SRS 6 ++#define DIG_ADC3_SRS 4 ++#define DIG_ADC2_SRS 2 ++#define DIG_ADC1_SRS 0 ++ ++/*ADC_DDT_CTRL*/ ++#define ADOUT_DLY_EN 2 ++#define ADOUT_DTS 0 ++ ++/*HPF_EN*/ ++#define DIG_ADC4_HPF_EN 3 ++#define DIG_ADC3_HPF_EN 2 ++#define DIG_ADC2_HPF_EN 1 ++#define DIG_ADC1_HPF_EN 0 ++ ++/*ADC1_DMIX_SRC*/ ++#define ADC1_ADC4_DMXL_GC 7 ++#define ADC1_ADC3_DMXL_GC 6 ++#define ADC1_ADC2_DMXL_GC 5 ++#define ADC1_ADC1_DMXL_GC 4 ++#define ADC1_ADC4_DMXL_SRC 3 ++#define ADC1_ADC3_DMXL_SRC 2 ++#define ADC1_ADC2_DMXL_SRC 1 ++#define ADC1_ADC1_DMXL_SRC 0 ++ ++/*ADC2_DMIX_SRC*/ ++#define ADC2_ADC4_DMXL_GC 7 ++#define ADC2_ADC3_DMXL_GC 6 ++#define ADC2_ADC2_DMXL_GC 5 ++#define ADC2_ADC1_DMXL_GC 4 ++#define ADC2_ADC4_DMXL_SRC 3 ++#define ADC2_ADC3_DMXL_SRC 2 ++#define ADC2_ADC2_DMXL_SRC 1 ++#define ADC2_ADC1_DMXL_SRC 0 ++ ++/*ADC3_DMIX_SRC*/ ++#define ADC3_ADC4_DMXL_GC 7 ++#define ADC3_ADC3_DMXL_GC 6 ++#define ADC3_ADC2_DMXL_GC 5 ++#define ADC3_ADC1_DMXL_GC 4 ++#define ADC3_ADC4_DMXL_SRC 3 ++#define ADC3_ADC3_DMXL_SRC 2 ++#define ADC3_ADC2_DMXL_SRC 1 ++#define ADC3_ADC1_DMXL_SRC 0 ++ ++/*ADC4_DMIX_SRC*/ ++#define ADC4_ADC4_DMXL_GC 7 ++#define ADC4_ADC3_DMXL_GC 6 ++#define ADC4_ADC2_DMXL_GC 5 ++#define ADC4_ADC1_DMXL_GC 4 ++#define ADC4_ADC4_DMXL_SRC 3 ++#define ADC4_ADC3_DMXL_SRC 2 ++#define ADC4_ADC2_DMXL_SRC 1 ++#define ADC4_ADC1_DMXL_SRC 0 ++ ++/*ADC_DIG_DEBUG*/ ++#define ADC_PTN_SEL 0 ++ ++/*I2S_DAT_PADDRV_CTRL*/ ++#define TX2_DAT_DRV 4 ++#define TX1_DAT_DRV 0 ++ ++/*I2S_CLK_PADDRV_CTRL*/ ++#define LRCK_DRV 4 ++#define BCLK_DRV 0 ++ ++/*ANA_PGA1_CTRL*/ ++#define ADC1_ANALOG_PGA 1 ++#define ADC1_ANALOG_PGA_STEP 0 ++ ++/*ANA_PGA2_CTRL*/ ++#define ADC2_ANALOG_PGA 1 ++#define ADC2_ANALOG_PGA_STEP 0 ++ ++/*ANA_PGA3_CTRL*/ ++#define ADC3_ANALOG_PGA 1 ++#define ADC3_ANALOG_PGA_STEP 0 ++ ++/*ANA_PGA4_CTRL*/ ++#define ADC4_ANALOG_PGA 1 ++#define ADC4_ANALOG_PGA_STEP 0 ++ ++ ++/*MIC_OFFSET_CTRL1*/ ++#define MIC_OFFSET_CAL_EN4 3 ++#define MIC_OFFSET_CAL_EN3 2 ++#define MIC_OFFSET_CAL_EN2 1 ++#define MIC_OFFSET_CAL_EN1 0 ++ ++/*MIC_OFFSET_CTRL2*/ ++#define MIC_OFFSET_CAL_GAIN 3 ++#define MIC_OFFSET_CAL_CHANNEL 1 ++#define MIC_OFFSET_CAL_EN_ONCE 0 ++ ++/*MIC1_OFFSET_STATU1*/ ++#define MIC1_OFFSET_CAL_DONE 7 ++#define MIC1_OFFSET_CAL_RUN_STA 6 ++#define MIC1_OFFSET_MSB 0 ++ ++/*MIC1_OFFSET_STATU2*/ ++#define MIC1_OFFSET_LSB 0 ++ ++/*MIC2_OFFSET_STATU1*/ ++#define MIC2_OFFSET_CAL_DONE 7 ++#define MIC2_OFFSET_CAL_RUN_STA 6 ++#define MIC2_OFFSET_MSB 0 ++ ++/*MIC2_OFFSET_STATU2*/ ++#define MIC2_OFFSET_LSB 0 ++ ++/*MIC3_OFFSET_STATU1*/ ++#define MIC3_OFFSET_CAL_DONE 7 ++#define MIC3_OFFSET_CAL_RUN_STA 6 ++#define MIC3_OFFSET_MSB 0 ++ ++/*MIC3_OFFSET_STATU2*/ ++#define MIC3_OFFSET_LSB 0 ++ ++/*MIC4_OFFSET_STATU1*/ ++#define MIC4_OFFSET_CAL_DONE 7 ++#define MIC4_OFFSET_CAL_RUN_STA 6 ++#define MIC4_OFFSET_MSB 0 ++ ++/*MIC4_OFFSET_STATU2*/ ++#define MIC4_OFFSET_LSB 0 ++ ++/*ANA_ADC1_CTRL1*/ ++#define ADC1_PGA_BYPASS 7 ++#define ADC1_PGA_BYP_RCM 6 ++#define ADC1_PGA_CTRL_RCM 4 ++#define ADC1_PGA_MUTE 3 ++#define ADC1_DSM_ENABLE 2 ++#define ADC1_PGA_ENABLE 1 ++#define ADC1_MICBIAS_EN 0 ++ ++/*ANA_ADC1_CTRL3*/ ++#define ADC1_ANA_CAL_EN 5 ++#define ADC1_SEL_OUT_EDGE 3 ++#define ADC1_DSM_DISABLE 2 ++#define ADC1_VREFP_DISABLE 1 ++#define ADC1_AAF_DISABLE 0 ++ ++/*ANA_ADC1_CTRL6*/ ++#define PGA_CTRL_TC 6 ++#define PGA_CTRL_RC 4 ++#define PGA_CTRL_I_LIN 2 ++#define PGA_CTRL_I_IN 0 ++ ++/*ANA_ADC1_CTRL7*/ ++#define PGA_CTRL_HI_Z 7 ++#define PGA_CTRL_SHORT_RF 6 ++#define PGA_CTRL_VCM_VG 4 ++#define PGA_CTRL_VCM_IN 0 ++ ++/*ANA_ADC2_CTRL1*/ ++#define ADC2_PGA_BYPASS 7 ++#define ADC2_PGA_BYP_RCM 6 ++#define ADC2_PGA_CTRL_RCM 4 ++#define ADC2_PGA_MUTE 3 ++#define ADC2_DSM_ENABLE 2 ++#define ADC2_PGA_ENABLE 1 ++#define ADC2_MICBIAS_EN 0 ++ ++/*ANA_ADC2_CTRL3*/ ++#define ADC2_ANA_CAL_EN 5 ++#define ADC2_SEL_OUT_EDGE 3 ++#define ADC2_DSM_DISABLE 2 ++#define ADC2_VREFP_DISABLE 1 ++#define ADC2_AAF_DISABLE 0 ++ ++/*ANA_ADC2_CTRL6*/ ++#define PGA_CTRL_IBOOST 7 ++#define PGA_CTRL_IQCTRL 6 ++#define PGA_CTRL_OABIAS 4 ++#define PGA_CTRL_CMLP_DIS 3 ++#define PGA_CTRL_PDB_RIN 2 ++#define PGA_CTRL_PEAKDET 0 ++ ++/*ANA_ADC2_CTRL7*/ ++#define AAF_LPMODE_EN 7 ++#define AAF_STG2_IB_SEL 4 ++#define AAFDSM_IB_DIV2 3 ++#define AAF_STG1_IB_SEL 0 ++ ++/*ANA_ADC3_CTRL1*/ ++#define ADC3_PGA_BYPASS 7 ++#define ADC3_PGA_BYP_RCM 6 ++#define ADC3_PGA_CTRL_RCM 4 ++#define ADC3_PGA_MUTE 3 ++#define ADC3_DSM_ENABLE 2 ++#define ADC3_PGA_ENABLE 1 ++#define ADC3_MICBIAS_EN 0 ++ ++/*ANA_ADC3_CTRL3*/ ++#define ADC3_ANA_CAL_EN 5 ++#define ADC3_INVERT_CLK 4 ++#define ADC3_SEL_OUT_EDGE 3 ++#define ADC3_DSM_DISABLE 2 ++#define ADC3_VREFP_DISABLE 1 ++#define ADC3_AAF_DISABLE 0 ++ ++/*ANA_ADC3_CTRL7*/ ++#define DSM_COMP_IB_SEL 6 ++#define DSM_OTA_CTRL 4 ++#define DSM_LPMODE 3 ++#define DSM_OTA_IB_SEL 0 ++ ++/*ANA_ADC4_CTRL1*/ ++#define ADC4_PGA_BYPASS 7 ++#define ADC4_PGA_BYP_RCM 6 ++#define ADC4_PGA_CTRL_RCM 4 ++#define ADC4_PGA_MUTE 3 ++#define ADC4_DSM_ENABLE 2 ++#define ADC4_PGA_ENABLE 1 ++#define ADC4_MICBIAS_EN 0 ++ ++/*ANA_ADC4_CTRL3*/ ++#define ADC4_ANA_CAL_EN 5 ++#define ADC4_SEL_OUT_EDGE 3 ++#define ADC4_DSM_DISABLE 2 ++#define ADC4_VREFP_DISABLE 1 ++#define ADC4_AAF_DISABLE 0 ++ ++/*ANA_ADC4_CTRL6*/ ++#define DSM_DEMOFF 5 ++#define DSM_EN_DITHER 4 ++#define DSM_VREFP_LPMODE 2 ++#define DSM_VREFP_OUTCTRL 0 ++ ++/*ANA_ADC4_CTRL7*/ ++#define CK8M_EN 5 ++#define OSC_EN 4 ++#define ADC4_CLK_GATING 3 ++#define ADC3_CLK_GATING 2 ++#define ADC2_CLK_GATING 1 ++#define ADC1_CLK_GATING 0 ++ ++/*GPIO_CFG1*/ ++#define GPIO2_SELECT 4 ++#define GPIO1_SELECT 0 ++ ++/*GPIO_CFG2*/ ++#define GPIO4_SELECT 4 ++#define GPIO3_SELECT 0 ++ ++/*GPIO_DAT*///order??? ++#define GPIO4_DAT 3 ++#define GPIO3_DAT 2 ++#define GPIO2_DAT 1 ++#define GPIO1_DAT 0 ++ ++/*GPIO_DRV*/ ++#define GPIO4_DRV 6 ++#define GPIO3_DRV 4 ++#define GPIO2_DRV 2 ++#define GPIO1_DRV 0 ++ ++/*GPIO_PULL*/ ++#define GPIO4_PULL 6 ++#define GPIO3_PULL 4 ++#define GPIO2_PULL 2 ++#define GPIO1_PULL 0 ++ ++/*GPIO_INT_CFG*/ ++#define GPIO4_EINT_CFG 6 ++#define GPIO3_EINT_CFG 4 ++#define GPIO2_EINT_CFG 2 ++#define GPIO1_EINT_CFG 0 ++ ++/*GPIO_INT_EN*///order??? ++#define GPIO4_EINT_EN 3 ++#define GPIO3_EINT_EN 2 ++#define GPIO2_EINT_EN 1 ++#define GPIO1_EINT_EN 0 ++ ++/*GPIO_INT_STATUS*///order??? ++#define GPIO4_EINT_STA 3 ++#define GPIO3_EINT_STA 2 ++#define GPIO2_EINT_STA 1 ++#define GPIO1_EINT_STA 0 ++ ++/*PRNG_CLK_CTRL*/ ++#define PRNG_CLK_EN 1 ++#define PRNG_CLK_POS 0 ++ ++/*** Some Config Value ***/ ++ ++//[SYSCLK_CTRL]: PLLCLK_SRC ++#define PLLCLK_SRC_MCLK 0 ++#define PLLCLK_SRC_BCLK 1 ++#define PLLCLK_SRC_GPIO2 2 ++#define PLLCLK_SRC_GPIO3 3 ++ ++//[SYSCLK_CTRL]: SYSCLK_SRC ++#define SYSCLK_SRC_MCLK 0 ++#define SYSCLK_SRC_PLL 1 ++ ++//I2S BCLK POLARITY Control ++#define BCLK_NORMAL_DRIVE_N_SAMPLE_P 0 ++#define BCLK_INVERT_DRIVE_P_SAMPLE_N 1 ++ ++//I2S LRCK POLARITY Control ++#define LRCK_LEFT_LOW_RIGHT_HIGH 0 ++#define LRCK_LEFT_HIGH_RIGHT_LOW 1 ++ ++//I2S Format Selection ++#define PCM_FORMAT 0 ++#define LEFT_JUSTIFIED_FORMAT 1 ++#define RIGHT_JUSTIFIED_FORMAT 2 ++ ++//I2S data protocol types ++ ++#define IS_ENCODING_MODE 0 ++ ++#endif ++ +--- /dev/null ++++ b/sound/soc/codecs/ac10x.h +@@ -0,0 +1,152 @@ ++/* ++ * ac10x.h ++ * ++ * (C) Copyright 2017-2018 ++ * Seeed Technology Co., Ltd. ++ * ++ * PeterYang ++ * ++ * (C) Copyright 2010-2017 ++ * Reuuimlla Technology Co., Ltd. ++ * huangxin ++ * ++ * some simple description for this code ++ * ++ * 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; either version 2 of ++ * the License, or (at your option) any later version. ++ * ++ */ ++#ifndef __AC10X_H__ ++#define __AC10X_H__ ++ ++#define AC101_I2C_ID 4 ++#define _MASTER_AC108 0 ++#define _MASTER_AC101 1 ++#define _MASTER_MULTI_CODEC _MASTER_AC101 ++ ++/* enable headset detecting & headset button pressing */ ++#define CONFIG_AC101_SWITCH_DETECT ++ ++/* obsolete */ ++#define CONFIG_AC10X_TRIG_LOCK 0 ++ ++#ifdef AC101_DEBG ++ #define AC101_DBG(format,args...) printk("[AC101] %s() L%d " format, __func__, __LINE__, ##args) ++#else ++ #define AC101_DBG(...) ++#endif ++ ++#include ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,18,0) ++#define __NO_SND_SOC_CODEC_DRV 1 ++#else ++#define __NO_SND_SOC_CODEC_DRV 0 ++#endif ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(5,4,0) ++#if __has_attribute(__fallthrough__) ++# define fallthrough __attribute__((__fallthrough__)) ++#else ++# define fallthrough do {} while (0) /* fallthrough */ ++#endif ++#endif ++ ++#if __NO_SND_SOC_CODEC_DRV ++#define codec component ++#define snd_soc_codec snd_soc_component ++#define snd_soc_codec_driver snd_soc_component_driver ++#define snd_soc_codec_get_drvdata snd_soc_component_get_drvdata ++#define snd_soc_codec_get_dapm snd_soc_component_get_dapm ++#define snd_soc_codec_get_bias_level snd_soc_component_get_bias_level ++#define snd_soc_kcontrol_codec snd_soc_kcontrol_component ++#define snd_soc_read snd_soc_component_read32 ++#define snd_soc_register_codec snd_soc_register_component ++#define snd_soc_unregister_codec snd_soc_unregister_component ++#define snd_soc_update_bits snd_soc_component_update_bits ++#define snd_soc_write snd_soc_component_write ++#define snd_soc_add_codec_controls snd_soc_add_component_controls ++#endif ++ ++ ++#ifdef CONFIG_AC101_SWITCH_DETECT ++enum headphone_mode_u { ++ HEADPHONE_IDLE, ++ FOUR_HEADPHONE_PLUGIN, ++ THREE_HEADPHONE_PLUGIN, ++}; ++#endif ++ ++struct ac10x_priv { ++ struct i2c_client *i2c[4]; ++ struct regmap* i2cmap[4]; ++ int codec_cnt; ++ unsigned sysclk; ++#define _FREQ_24_576K 24576000 ++#define _FREQ_22_579K 22579200 ++ unsigned mclk; /* master clock or aif_clock/aclk */ ++ int clk_id; ++ unsigned char i2s_mode; ++ unsigned char data_protocol; ++ struct delayed_work dlywork; ++ int tdm_chips_cnt; ++ int sysclk_en; ++ ++ /* member for ac101 .begin */ ++ struct snd_soc_codec *codec; ++ struct i2c_client *i2c101; ++ struct regmap* regmap101; ++ ++ struct mutex dac_mutex; ++ u8 dac_enable; ++ spinlock_t lock; ++ u8 aif1_clken; ++ ++ struct work_struct codec_resume; ++ struct gpio_desc* gpiod_spk_amp_gate; ++ ++ #ifdef CONFIG_AC101_SWITCH_DETECT ++ struct gpio_desc* gpiod_irq; ++ long irq; ++ volatile int irq_cntr; ++ volatile int pullout_cntr; ++ volatile int state; ++ ++ enum headphone_mode_u mode; ++ struct work_struct work_switch; ++ struct work_struct work_clear_irq; ++ ++ struct input_dev* inpdev; ++ #endif ++ /* member for ac101 .end */ ++}; ++ ++ ++/* AC101 DAI operations */ ++int ac101_audio_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai); ++void ac101_aif_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai); ++int ac101_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt); ++int ac101_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params, ++ struct snd_soc_dai *codec_dai); ++int ac101_trigger(struct snd_pcm_substream *substream, int cmd, ++ struct snd_soc_dai *dai); ++int ac101_aif_mute(struct snd_soc_dai *codec_dai, int mute); ++ ++/* codec driver specific */ ++int ac101_codec_probe(struct snd_soc_codec *codec); ++int ac101_codec_remove(struct snd_soc_codec *codec); ++int ac101_codec_suspend(struct snd_soc_codec *codec); ++int ac101_codec_resume(struct snd_soc_codec *codec); ++int ac101_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level); ++ ++/* i2c device specific */ ++int ac101_probe(struct i2c_client *i2c, const struct i2c_device_id *id); ++void ac101_shutdown(struct i2c_client *i2c); ++int ac101_remove(struct i2c_client *i2c); ++ ++int ac10x_fill_regcache(struct device* dev, struct regmap* map); ++ ++#endif//__AC10X_H__ diff --git a/target/linux/starfive/patches-6.6/0080-ASoC-starfive-Add-SPDIF-and-PCM-driver.patch b/target/linux/starfive/patches-6.6/0080-ASoC-starfive-Add-SPDIF-and-PCM-driver.patch new file mode 100644 index 0000000000..f2fb206947 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0080-ASoC-starfive-Add-SPDIF-and-PCM-driver.patch @@ -0,0 +1,1169 @@ +From f03b7c834baef87e4f740e10a8bbcbfc57bd985a Mon Sep 17 00:00:00 2001 +From: Xingyu Wu +Date: Thu, 15 Jun 2023 11:32:50 +0800 +Subject: [PATCH 080/116] ASoC: starfive: Add SPDIF and PCM driver + +Add SPDIF and SPDIF-PCM driver for StarFive JH7110. + +Signed-off-by: Xingyu Wu +Signed-off-by: Hal Feng +--- + sound/soc/starfive/Kconfig | 17 + + sound/soc/starfive/Makefile | 5 + + sound/soc/starfive/jh7110_spdif.c | 568 ++++++++++++++++++++++++++ + sound/soc/starfive/jh7110_spdif.h | 196 +++++++++ + sound/soc/starfive/jh7110_spdif_pcm.c | 339 +++++++++++++++ + 5 files changed, 1125 insertions(+) + create mode 100644 sound/soc/starfive/jh7110_spdif.c + create mode 100644 sound/soc/starfive/jh7110_spdif.h + create mode 100644 sound/soc/starfive/jh7110_spdif_pcm.c + +--- a/sound/soc/starfive/Kconfig ++++ b/sound/soc/starfive/Kconfig +@@ -16,6 +16,23 @@ config SND_SOC_JH7110_PWMDAC + Say Y or M if you want to add support for StarFive JH7110 + PWM-DAC driver. + ++config SND_SOC_JH7110_SPDIF ++ tristate "JH7110 SPDIF module" ++ depends on HAVE_CLK && SND_SOC_STARFIVE ++ select SND_SOC_GENERIC_DMAENGINE_PCM ++ select REGMAP_MMIO ++ help ++ Say Y or M if you want to add support for SPDIF driver of StarFive ++ JH7110 SoC. ++ ++config SND_SOC_JH7110_SPDIF_PCM ++ bool "PCM PIO extension for JH7110 SPDIF" ++ depends on SND_SOC_JH7110_SPDIF ++ default y if SND_SOC_JH7110_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_SOC_JH7110_TDM + tristate "JH7110 TDM device driver" + depends on HAVE_CLK && SND_SOC_STARFIVE +--- a/sound/soc/starfive/Makefile ++++ b/sound/soc/starfive/Makefile +@@ -1,3 +1,8 @@ + # StarFive Platform Support + obj-$(CONFIG_SND_SOC_JH7110_PWMDAC) += jh7110_pwmdac.o ++ ++obj-$(CONFIG_SND_SOC_JH7110_SPDIF) += spdif.o ++spdif-y := jh7110_spdif.o ++spdif-$(CONFIG_SND_SOC_JH7110_SPDIF_PCM) += jh7110_spdif_pcm.o ++ + obj-$(CONFIG_SND_SOC_JH7110_TDM) += jh7110_tdm.o +--- /dev/null ++++ b/sound/soc/starfive/jh7110_spdif.c +@@ -0,0 +1,568 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * SPDIF driver for the StarFive JH7110 SoC ++ * ++ * Copyright (C) 2022 StarFive Technology Co., Ltd. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "jh7110_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 = 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: ++ dev_err(dai->dev, "%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 = params_channels(params); ++ unsigned int rate = params_rate(params); ++ unsigned int format = params_format(params); ++ unsigned int tsamplerate; ++ unsigned int mclk; ++ unsigned int audio_root; ++ int ret; ++ ++ switch (channels) { ++ case 1: ++ regmap_update_bits(spdif->regmap, SPDIF_CTRL, ++ SPDIF_CHANNEL_MODE, SPDIF_CHANNEL_MODE); ++ regmap_update_bits(spdif->regmap, SPDIF_CTRL, ++ SPDIF_DUPLICATE, SPDIF_DUPLICATE); ++ spdif->channels = false; ++ break; ++ case 2: ++ regmap_update_bits(spdif->regmap, SPDIF_CTRL, ++ SPDIF_CHANNEL_MODE, 0); ++ spdif->channels = true; ++ 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_S24_3LE: ++ case SNDRV_PCM_FORMAT_S32_LE: ++ break; ++ default: ++ dev_err(dai->dev, "invalid format\n"); ++ return -EINVAL; ++ } ++ ++ switch (rate) { ++ case 8000: ++ break; ++ case 11025: ++ audio_root = 148500000; ++ /* 11025 * 512 = 5644800 */ ++ /* But now pll2 is 1188m and mclk should be 5711539 closely. */ ++ mclk = 5711539; ++ break; ++ case 16000: ++ break; ++ case 22050: ++ audio_root = 148500000; ++ mclk = 11423077; ++ break; ++ default: ++ dev_err(dai->dev, "channel:%d sample rate:%d\n", channels, rate); ++ return -EINVAL; ++ } ++ ++ /* use mclk_inner clock from 1188m PLL2 will be better about 11k and 22k*/ ++ if ((rate == 11025) || (rate == 22050)) { ++ ret = clk_set_parent(spdif->mclk, spdif->mclk_inner); ++ if (ret) { ++ dev_err(dai->dev, ++ "failed to set parent to mclk_inner ret=%d\n", ret); ++ goto fail_ext; ++ } ++ ++ ret = clk_set_rate(spdif->audio_root, audio_root); ++ if (ret) { ++ dev_err(dai->dev, "failed to set audio_root rate :%d\n", ret); ++ goto fail_ext; ++ } ++ ++ ret = clk_set_rate(spdif->mclk_inner, mclk); ++ if (ret) { ++ dev_err(dai->dev, "failed to set mclk_inner rate :%d\n", ret); ++ goto fail_ext; ++ } ++ ++ mclk = clk_get_rate(spdif->mclk_inner); ++ } else { ++ ret = clk_set_parent(spdif->mclk, spdif->mclk_ext); ++ if (ret) { ++ dev_err(dai->dev, ++ "failed to set parent to mclk_ext ret=%d\n", ret); ++ goto fail_ext; ++ } ++ ++ mclk = clk_get_rate(spdif->mclk_ext); ++ } ++ ++ /* (FCLK)4096000/128=32000 */ ++ tsamplerate = (mclk / 128 + rate / 2) / rate - 1; ++ if (tsamplerate < 3) ++ tsamplerate = 3; ++ ++ /* transmission sample rate */ ++ regmap_update_bits(spdif->regmap, SPDIF_CTRL, 0xFF, tsamplerate); ++ ++ return 0; ++ ++fail_ext: ++ return ret; ++} ++ ++static int sf_spdif_clks_get(struct platform_device *pdev, ++ struct sf_spdif_dev *spdif) ++{ ++ static struct clk_bulk_data clks[] = { ++ { .id = "apb" }, /* clock-names in dts file */ ++ { .id = "core" }, ++ { .id = "audroot" }, ++ { .id = "mclk_inner"}, ++ { .id = "mclk_ext"}, ++ { .id = "mclk"}, ++ }; ++ int ret = devm_clk_bulk_get(&pdev->dev, ARRAY_SIZE(clks), clks); ++ ++ spdif->spdif_apb = clks[0].clk; ++ spdif->spdif_core = clks[1].clk; ++ spdif->audio_root = clks[2].clk; ++ spdif->mclk_inner = clks[3].clk; ++ spdif->mclk_ext = clks[4].clk; ++ spdif->mclk = clks[5].clk; ++ ++ return ret; ++} ++ ++static int sf_spdif_resets_get(struct platform_device *pdev, ++ struct sf_spdif_dev *spdif) ++{ ++ struct reset_control_bulk_data resets[] = { ++ { .id = "apb" }, ++ }; ++ int ret = devm_reset_control_bulk_get_exclusive(&pdev->dev, ARRAY_SIZE(resets), resets); ++ ++ if (ret) ++ return ret; ++ ++ spdif->rst_apb = resets[0].rstc; ++ ++ return 0; ++} ++ ++static int starfive_spdif_crg_enable(struct sf_spdif_dev *spdif, bool enable) ++{ ++ int ret; ++ ++ dev_dbg(spdif->dev, "starfive_spdif clk&rst %sable.\n", enable ? "en":"dis"); ++ if (enable) { ++ ret = clk_prepare_enable(spdif->spdif_apb); ++ if (ret) { ++ dev_err(spdif->dev, "failed to prepare enable spdif_apb\n"); ++ goto failed_apb_clk; ++ } ++ ++ ret = clk_prepare_enable(spdif->spdif_core); ++ if (ret) { ++ dev_err(spdif->dev, "failed to prepare enable spdif_core\n"); ++ goto failed_core_clk; ++ } ++ ++ ret = reset_control_deassert(spdif->rst_apb); ++ if (ret) { ++ dev_err(spdif->dev, "failed to deassert apb\n"); ++ goto failed_rst; ++ } ++ } else { ++ clk_disable_unprepare(spdif->spdif_core); ++ clk_disable_unprepare(spdif->spdif_apb); ++ } ++ ++ return 0; ++ ++failed_rst: ++ clk_disable_unprepare(spdif->spdif_core); ++failed_core_clk: ++ clk_disable_unprepare(spdif->spdif_apb); ++failed_apb_clk: ++ return ret; ++} ++ ++static int sf_spdif_dai_probe(struct snd_soc_dai *dai) ++{ ++ struct sf_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai); ++ ++ pm_runtime_get_sync(spdif->dev); ++ ++ /* 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, ++ BIT8TO20MASK<regmap, SPDIF_FIFO_CTRL, ++ ALLBITMASK, 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); ++ ++ pm_runtime_put_sync(spdif->dev); ++ return 0; ++} ++ ++static const struct snd_soc_dai_ops sf_spdif_dai_ops = { ++ .probe = sf_spdif_dai_probe, ++ .trigger = sf_spdif_trigger, ++ .hw_params = sf_spdif_hw_params, ++}; ++ ++#ifdef CONFIG_PM_SLEEP ++static int spdif_system_suspend(struct device *dev) ++{ ++ struct sf_spdif_dev *spdif = dev_get_drvdata(dev); ++ ++ /* save the register value */ ++ regmap_read(spdif->regmap, SPDIF_CTRL, &spdif->reg_spdif_ctrl); ++ regmap_read(spdif->regmap, SPDIF_INT_REG, &spdif->reg_spdif_int); ++ regmap_read(spdif->regmap, SPDIF_FIFO_CTRL, &spdif->reg_spdif_fifo_ctrl); ++ ++ return pm_runtime_force_suspend(dev); ++} ++ ++static int spdif_system_resume(struct device *dev) ++{ ++ struct sf_spdif_dev *spdif = dev_get_drvdata(dev); ++ int ret = pm_runtime_force_resume(dev); ++ ++ if (ret) ++ return ret; ++ ++ /* restore the register value */ ++ regmap_update_bits(spdif->regmap, SPDIF_CTRL, ++ ALLBITMASK, spdif->reg_spdif_ctrl); ++ regmap_update_bits(spdif->regmap, SPDIF_INT_REG, ++ ALLBITMASK, spdif->reg_spdif_int); ++ regmap_update_bits(spdif->regmap, SPDIF_FIFO_CTRL, ++ ALLBITMASK, spdif->reg_spdif_fifo_ctrl); ++ ++ return 0; ++} ++#endif ++ ++#ifdef CONFIG_PM ++static int spdif_runtime_suspend(struct device *dev) ++{ ++ struct sf_spdif_dev *spdif = dev_get_drvdata(dev); ++ ++ return starfive_spdif_crg_enable(spdif, false); ++} ++ ++static int spdif_runtime_resume(struct device *dev) ++{ ++ struct sf_spdif_dev *spdif = dev_get_drvdata(dev); ++ ++ return starfive_spdif_crg_enable(spdif, true); ++} ++#endif ++ ++static const struct dev_pm_ops spdif_pm_ops = { ++ SET_RUNTIME_PM_OPS(spdif_runtime_suspend, spdif_runtime_resume, NULL) ++ SET_SYSTEM_SLEEP_PM_OPS(spdif_system_suspend, spdif_system_resume) ++}; ++ ++#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, ++ .playback = { ++ .stream_name = "Playback", ++ .channels_min = 1, ++ .channels_max = 2, ++ .rates = SF_PCM_RATE_8000_22050, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE | ++ SNDRV_PCM_FMTBIT_S24_LE | ++ SNDRV_PCM_FMTBIT_S24_3LE | ++ SNDRV_PCM_FMTBIT_S32_LE, ++ }, ++ .ops = &sf_spdif_dai_ops, ++ .symmetric_rate = 1, ++}; ++ ++static const struct snd_soc_component_driver sf_spdif_component = { ++ .name = "starfive-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; ++ ++ ret = sf_spdif_clks_get(pdev, spdif); ++ if (ret) { ++ dev_err(&pdev->dev, "failed to get audio clock\n"); ++ return ret; ++ } ++ ++ ret = sf_spdif_resets_get(pdev, spdif); ++ if (ret) { ++ dev_err(&pdev->dev, "failed to get audio reset controls\n"); ++ return ret; ++ } ++ ++ ret = starfive_spdif_crg_enable(spdif, true); ++ if (ret) { ++ dev_err(&pdev->dev, "failed to enable audio clock\n"); ++ return ret; ++ } ++ ++ 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; ++ ++ starfive_spdif_crg_enable(spdif, false); ++ pm_runtime_enable(&pdev->dev); ++ dev_dbg(&pdev->dev, "spdif register done.\n"); ++ ++ return 0; ++ ++err_clk_disable: ++ return ret; ++} ++ ++static const struct of_device_id sf_spdif_of_match[] = { ++ { .compatible = "starfive,jh7110-spdif", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, sf_spdif_of_match); ++ ++static struct platform_driver sf_spdif_driver = { ++ .driver = { ++ .name = "starfive-spdif", ++ .of_match_table = sf_spdif_of_match, ++ .pm = &spdif_pm_ops, ++ }, ++ .probe = sf_spdif_probe, ++}; ++module_platform_driver(sf_spdif_driver); ++ ++MODULE_AUTHOR("curry.zhang "); ++MODULE_AUTHOR("Xingyu Wu "); ++MODULE_DESCRIPTION("starfive SPDIF driver"); ++MODULE_LICENSE("GPL v2"); +--- /dev/null ++++ b/sound/soc/starfive/jh7110_spdif.h +@@ -0,0 +1,196 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * SPDIF driver for the StarFive JH7110 SoC ++ * ++ * Copyright (C) 2022 StarFive Technology Co., Ltd. ++ */ ++ ++#ifndef __SND_SOC_JH7110_SPDIF_H ++#define __SND_SOC_JH7110_SPDIF_H ++ ++#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] */ ++/* 0:SFR reg reset to defualt value; auto set back to '1' after reset */ ++#define SPDIF_SFR_ENABLE (1<<8) ++/* 0:reset of SPDIF block, SRF bits are unchanged; 1:enables SPDIF module */ ++#define SPDIF_ENABLE (1<<9) ++/* 0:FIFO pointers are reset to zero,threshold levels for FIFO are unchaned; auto set back to 1 */ ++#define SPDIF_FIFO_ENABLE (1<<10) ++/* 1:blocked and the modules are in power save mode; 0:block feeds the modules */ ++#define SPDIF_CLK_ENABLE (1<<11) ++#define SPDIF_TR_MODE (1<<12) /* 0:rx; 1:tx */ ++/* 0:party bit rx in a sub-frame is repeated on the parity; 1:check on a parity error */ ++#define SPDIF_PARITCHECK (1<<13) ++/* ++ * 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_PARITYGEN (1<<14) ++/* 0:validity bit in frame isn't checked and all frame are written; 1:validity bit rx is checked */ ++#define SPDIF_VALIDITYCHECK (1<<15) ++#define SPDIF_CHANNEL_MODE (1<<16) /* 0:two-channel; 1:single-channel */ ++/* only tx -single-channel mode; 0:secondary channel; 1: left(primary) channel */ ++#define SPDIF_DUPLICATE (1<<17) ++/* ++ * only tx; ++ * 0:first preamble B after reset tx valid sub-frame; ++ * 1:first preamble B is tx after preambleddel(INT_REG) ++ */ ++#define SPDIF_SETPREAMBB (1<<18) ++/* 0:FIFO disabled ,APB accese FIFO; 1:FIFO enable, APB access to FIFO disable; */ ++#define SPDIF_USE_FIFO_IF (1<<19) ++#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 */ ++/* 1:left channel received and tx into FIFO; 0:right channel received and tx into FIFO */ ++#define SPDIF_RIGHT_LEFT (1<<31) ++ ++#define BIT8TO20MASK 0x1FFF ++#define ALLBITMASK 0xFFFFFFFF ++ ++#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; ++ bool channels; ++ unsigned int tx_ptr; ++ unsigned int rx_ptr; ++ struct clk *spdif_apb; ++ struct clk *spdif_core; ++ struct clk *audio_root; ++ struct clk *mclk_inner; ++ struct clk *mclk; ++ struct clk *mclk_ext; ++ struct reset_control *rst_apb; ++ unsigned int reg_spdif_ctrl; ++ unsigned int reg_spdif_int; ++ unsigned int reg_spdif_fifo_ctrl; ++ ++ struct snd_dmaengine_dai_dma_data dma_data; ++}; ++ ++#if IS_ENABLED(CONFIG_SND_SOC_JH7110_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_JH7110_SPDIF_H */ +--- /dev/null ++++ b/sound/soc/starfive/jh7110_spdif_pcm.c +@@ -0,0 +1,339 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * SPDIF PCM driver for the StarFive JH7110 SoC ++ * ++ * Copyright (C) 2022 StarFive Technology Co., Ltd. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "jh7110_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) ++{ ++ unsigned int period_pos = tx_ptr % runtime->period_size; ++ u32 data[2]; ++ int i; ++ ++ /* two- channel and signal-channel mode */ ++ if (dev->channels) { ++ const u16 (*p16)[2] = (void *)runtime->dma_area; ++ const u32 (*p32)[2] = (void *)runtime->dma_area; ++ ++ for (i = 0; i < dev->fifo_th; i++) { ++ if (format == SNDRV_PCM_FORMAT_S16_LE) { ++ data[0] = p16[tx_ptr][0]; ++ data[0] = data[0]<<8; ++ data[0] &= 0x00ffff00; ++ data[1] = p16[tx_ptr][1]; ++ data[1] = data[1]<<8; ++ data[1] &= 0x00ffff00; ++ } else if (format == SNDRV_PCM_FORMAT_S24_LE) { ++ data[0] = p32[tx_ptr][0]; ++ data[1] = p32[tx_ptr][1]; ++ ++ /* ++ * To adapt S24_3LE and ALSA pass parameter of S24_LE. ++ * operation of S24_LE should be same to S24_3LE. ++ * So it would wrong when playback S24_LE file. ++ * when want to playback S24_LE file, should add in there: ++ * data[0] = data[0]>>8; ++ * data[1] = data[1]>>8; ++ */ ++ ++ data[0] &= 0x00ffffff; ++ data[1] &= 0x00ffffff; ++ } else if (format == SNDRV_PCM_FORMAT_S24_3LE) { ++ data[0] = p32[tx_ptr][0]; ++ data[1] = p32[tx_ptr][1]; ++ data[0] &= 0x00ffffff; ++ data[1] &= 0x00ffffff; ++ } else if (format == SNDRV_PCM_FORMAT_S32_LE) { ++ data[0] = p32[tx_ptr][0]; ++ data[0] = data[0]>>8; ++ data[1] = p32[tx_ptr][1]; ++ 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; ++ } ++ } else { ++ const u16 (*p16) = (void *)runtime->dma_area; ++ const u32 (*p32) = (void *)runtime->dma_area; ++ ++ for (i = 0; i < dev->fifo_th; i++) { ++ if (format == SNDRV_PCM_FORMAT_S16_LE) { ++ data[0] = p16[tx_ptr]; ++ data[0] = data[0]<<8; ++ data[0] &= 0x00ffff00; ++ } else if (format == SNDRV_PCM_FORMAT_S24_LE || ++ format == SNDRV_PCM_FORMAT_S24_3LE) { ++ data[0] = p32[tx_ptr]; ++ data[0] &= 0x00ffffff; ++ } else if (format == SNDRV_PCM_FORMAT_S32_LE) { ++ data[0] = p32[tx_ptr]; ++ data[0] = data[0]>>8; ++ } ++ ++ iowrite32(data[0], 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; ++ unsigned int period_pos = rx_ptr % runtime->period_size; ++ u32 data[2]; ++ 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 (format == SNDRV_PCM_FORMAT_S16_LE) { ++ p16[rx_ptr][0] = data[0]>>8; ++ p16[rx_ptr][1] = data[1]>>8; ++ } else if (format == SNDRV_PCM_FORMAT_S24_LE) { ++ p32[rx_ptr][0] = data[0]; ++ p32[rx_ptr][1] = data[1]; ++ } else if (format == SNDRV_PCM_FORMAT_S32_LE) { ++ 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 | ++ SNDRV_PCM_INFO_PAUSE | ++ SNDRV_PCM_INFO_RESUME, ++ .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_S24_3LE | ++ SNDRV_PCM_FMTBIT_S32_LE, ++ .channels_min = 1, ++ .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; ++ ++ switch (params_channels(hw_params)) { ++ case 1: ++ 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_S24_3LE: ++ 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); ++} diff --git a/target/linux/starfive/patches-6.6/0081-ASoC-starfive-Add-JH7110-PDM-driver.patch b/target/linux/starfive/patches-6.6/0081-ASoC-starfive-Add-JH7110-PDM-driver.patch new file mode 100644 index 0000000000..006760d754 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0081-ASoC-starfive-Add-JH7110-PDM-driver.patch @@ -0,0 +1,537 @@ +From 9c4858f9fe4d8f8fe5cf347b3ca727016b7ba492 Mon Sep 17 00:00:00 2001 +From: Walker Chen +Date: Tue, 20 Jun 2023 15:57:53 +0800 +Subject: [PATCH 081/116] ASoC: starfive: Add JH7110 PDM driver + +Add pdm driver support for the StarFive JH7110 SoC. + +Signed-off-by: Walker Chen +--- + sound/soc/starfive/Kconfig | 8 + + sound/soc/starfive/Makefile | 2 + + sound/soc/starfive/jh7110_pdm.c | 493 ++++++++++++++++++++++++++++++++ + 3 files changed, 503 insertions(+) + create mode 100644 sound/soc/starfive/jh7110_pdm.c + +--- a/sound/soc/starfive/Kconfig ++++ b/sound/soc/starfive/Kconfig +@@ -7,6 +7,14 @@ config SND_SOC_STARFIVE + the Starfive SoCs' Audio interfaces. You will also need to + select the audio interfaces to support below. + ++config SND_SOC_JH7110_PDM ++ tristate "JH7110 PDM device driver" ++ depends on HAVE_CLK && SND_SOC_STARFIVE ++ select SND_SOC_JH7110_I2S ++ select REGMAP_MMIO ++ help ++ Say Y or M if you want to add support for StarFive pdm driver. ++ + config SND_SOC_JH7110_PWMDAC + tristate "JH7110 PWM-DAC device driver" + depends on HAVE_CLK && SND_SOC_STARFIVE +--- a/sound/soc/starfive/Makefile ++++ b/sound/soc/starfive/Makefile +@@ -1,4 +1,6 @@ + # StarFive Platform Support ++obj-$(CONFIG_SND_SOC_JH7110_PDM) += jh7110_pdm.o ++ + obj-$(CONFIG_SND_SOC_JH7110_PWMDAC) += jh7110_pwmdac.o + + obj-$(CONFIG_SND_SOC_JH7110_SPDIF) += spdif.o +--- /dev/null ++++ b/sound/soc/starfive/jh7110_pdm.c +@@ -0,0 +1,493 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * PDM driver for the StarFive JH7110 SoC ++ * ++ * Copyright (C) 2022 StarFive Technology Co., Ltd. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#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 1 ++#define PDM_DMIC_MSB_MASK (0x7 << PDM_DMIC_MSB_SHIFT) ++#define PDM_DMIC_VOL_SHIFT 16 ++#define PDM_DMIC_VOL_MASK (0x3f << PDM_DMIC_VOL_SHIFT) ++#define PDM_VOL_DB_MUTE (0x3f << PDM_DMIC_VOL_SHIFT) ++#define PDM_VOL_DB_MAX 0 ++ ++#define PDM_DMIC_RVOL_MASK BIT(22) ++#define PDM_DMIC_LVOL_MASK BIT(23) ++#define PDM_DMIC_I2S_SLAVE BIT(24) ++#define PDM_DMIC_HPF_EN BIT(28) ++#define PDM_DMIC_FASTMODE_MASK BIT(29) ++#define PDM_DMIC_DC_BYPASS_MASK BIT(30) ++#define PDM_SW_RST_MASK BIT(31) ++#define PDM_SW_RST_RELEASE BIT(31) ++ ++/* PDM SCALE OFFSET */ ++#define DMIC_DCOFF3_SHIFT 24 ++#define DMIC_DCOFF2_SHIFT 16 ++#define DMIC_DCOFF1_SHIFT 8 ++ ++#define DMIC_DCOFF3_MASK (0xf << DMIC_DCOFF3_SHIFT) ++#define DMIC_DCOFF3_VAL (0xc << DMIC_DCOFF3_SHIFT) ++#define DMIC_DCOFF1_MASK (0xff << DMIC_DCOFF1_SHIFT) ++#define DMIC_DCOFF1_VAL (0x5 << DMIC_DCOFF1_SHIFT) ++#define DMIC_SCALE_MASK 0x3f ++#define DMIC_SCALE_DEF_VAL 0x8 ++ ++enum PDM_MSB_SHIFT { ++ PDM_MSB_SHIFT_NONE = 0, ++ PDM_MSB_SHIFT_1, ++ PDM_MSB_SHIFT_2, ++ PDM_MSB_SHIFT_3, ++ PDM_MSB_SHIFT_4, ++ PDM_MSB_SHIFT_5, ++ PDM_MSB_SHIFT_6, ++ PDM_MSB_SHIFT_7, ++}; ++ ++struct sf_pdm { ++ struct regmap *pdm_map; ++ struct device *dev; ++ struct clk *clk_pdm_apb; ++ struct clk *clk_pdm_mclk; ++ struct clk *clk_mclk; ++ struct clk *clk_mclk_ext; ++ struct reset_control *rst_pdm_dmic; ++ struct reset_control *rst_pdm_apb; ++ unsigned char flag_first; ++ unsigned int saved_ctrl0; ++ unsigned int saved_scale0; ++}; ++ ++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 void sf_pdm_enable(struct regmap *map) ++{ ++ /* Left and Right Channel Volume Control Enable */ ++ regmap_update_bits(map, PDM_DMIC_CTRL0, PDM_DMIC_RVOL_MASK, 0); ++ regmap_update_bits(map, PDM_DMIC_CTRL0, PDM_DMIC_LVOL_MASK, 0); ++} ++ ++static void sf_pdm_disable(struct regmap *map) ++{ ++ /* Left and Right Channel Volume Control Disable */ ++ regmap_update_bits(map, PDM_DMIC_CTRL0, ++ PDM_DMIC_RVOL_MASK, PDM_DMIC_RVOL_MASK); ++ regmap_update_bits(map, PDM_DMIC_CTRL0, ++ PDM_DMIC_LVOL_MASK, PDM_DMIC_LVOL_MASK); ++} ++ ++static int sf_pdm_trigger(struct snd_pcm_substream *substream, int cmd, ++ struct snd_soc_dai *dai) ++{ ++ struct sf_pdm *priv = snd_soc_dai_get_drvdata(dai); ++ ++ switch (cmd) { ++ case SNDRV_PCM_TRIGGER_START: ++ case SNDRV_PCM_TRIGGER_RESUME: ++ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: ++ if (priv->flag_first) { ++ priv->flag_first = 0; ++ mdelay(200); ++ } ++ ++ sf_pdm_enable(priv->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 sample_rate; ++ unsigned int data_width; ++ int ret; ++ const int pdm_mul = 128; ++ ++ sample_rate = params_rate(params); ++ switch (sample_rate) { ++ case 8000: ++ case 11025: ++ case 16000: ++ break; ++ default: ++ dev_err(priv->dev, "can't support sample rate:%d\n", sample_rate); ++ return -EINVAL; ++ } ++ ++ data_width = params_width(params); ++ switch (data_width) { ++ case 16: ++ case 32: ++ break; ++ default: ++ dev_err(priv->dev, "can't support bit width %d\n", data_width); ++ return -EINVAL; ++ } ++ ++ /* set pdm_mclk, PDM MCLK = 128 * LRCLK */ ++ ret = clk_set_rate(priv->clk_pdm_mclk, pdm_mul * sample_rate); ++ if (ret) { ++ dev_err(priv->dev, "Can't set pdm_mclk: %d\n", ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static const struct snd_soc_dai_ops sf_pdm_dai_ops = { ++ .trigger = sf_pdm_trigger, ++ .hw_params = sf_pdm_hw_params, ++}; ++ ++static void sf_pdm_module_init(struct sf_pdm *priv) ++{ ++ /* Reset */ ++ regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0, ++ PDM_SW_RST_MASK, 0x00); ++ regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0, ++ PDM_SW_RST_MASK, PDM_SW_RST_RELEASE); ++ ++ /* Make sure the device is initially disabled */ ++ sf_pdm_disable(priv->pdm_map); ++ ++ /* MUTE */ ++ regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0, ++ PDM_DMIC_VOL_MASK, PDM_VOL_DB_MUTE); ++ ++ /* UNMUTE */ ++ regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0, ++ PDM_DMIC_VOL_MASK, PDM_VOL_DB_MAX); ++ ++ /* enable high pass filter */ ++ regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0, ++ PDM_DMIC_HPF_EN, PDM_DMIC_HPF_EN); ++ ++ /* PDM work as slave mode */ ++ regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0, ++ PDM_DMIC_I2S_SLAVE, PDM_DMIC_I2S_SLAVE); ++ ++ /* disable fast mode */ ++ regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0, ++ PDM_DMIC_FASTMODE_MASK, 0); ++ ++ /* dmic msb shift 0 */ ++ regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0, ++ PDM_DMIC_MSB_MASK, 0); ++ ++ /* scale: 0x8 */ ++ regmap_update_bits(priv->pdm_map, PDM_DC_SCALE0, ++ DMIC_SCALE_MASK, DMIC_SCALE_DEF_VAL); ++ ++ regmap_update_bits(priv->pdm_map, PDM_DC_SCALE0, ++ DMIC_DCOFF1_MASK, DMIC_DCOFF1_VAL); ++ ++ regmap_update_bits(priv->pdm_map, PDM_DC_SCALE0, ++ DMIC_DCOFF3_MASK, DMIC_DCOFF3_VAL); ++ ++ /* scale: 0x3f */ ++ regmap_update_bits(priv->pdm_map, PDM_DC_SCALE0, ++ DMIC_SCALE_MASK, DMIC_SCALE_MASK); ++ ++ /* dmic msb shift 2 */ ++ regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0, ++ PDM_DMIC_MSB_MASK, PDM_MSB_SHIFT_4); ++} ++ ++#define SF_PDM_RATES (SNDRV_PCM_RATE_8000 | \ ++ SNDRV_PCM_RATE_11025 | \ ++ SNDRV_PCM_RATE_16000) ++ ++#define SF_PDM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ ++ SNDRV_PCM_FMTBIT_S32_LE) ++ ++static struct snd_soc_dai_driver sf_pdm_dai_drv = { ++ .name = "PDM", ++ .id = 0, ++ .capture = { ++ .stream_name = "Capture", ++ .channels_min = 2, ++ .channels_max = 2, ++ .rates = SF_PDM_RATES, ++ .formats = SF_PDM_FORMATS, ++ }, ++ .ops = &sf_pdm_dai_ops, ++ .symmetric_rate = 1, ++}; ++ ++static int sf_pdm_component_probe(struct snd_soc_component *component) ++{ ++ struct sf_pdm *priv = snd_soc_component_get_drvdata(component); ++ ++ snd_soc_component_init_regmap(component, priv->pdm_map); ++ snd_soc_add_component_controls(component, sf_pdm_snd_controls, ++ ARRAY_SIZE(sf_pdm_snd_controls)); ++ ++ return 0; ++} ++ ++static int sf_pdm_clock_enable(struct sf_pdm *priv) ++{ ++ int ret; ++ ++ ret = clk_prepare_enable(priv->clk_pdm_mclk); ++ if (ret) { ++ dev_err(priv->dev, "failed to prepare enable clk_pdm_mclk\n"); ++ return ret; ++ } ++ ++ ret = clk_prepare_enable(priv->clk_pdm_apb); ++ if (ret) { ++ dev_err(priv->dev, "failed to prepare enable clk_pdm_apb\n"); ++ goto disable_pdm_mclk; ++ } ++ ++ ret = reset_control_deassert(priv->rst_pdm_dmic); ++ if (ret) { ++ dev_err(priv->dev, "failed to deassert pdm_dmic\n"); ++ goto disable_pdm_apb; ++ } ++ ++ ret = reset_control_deassert(priv->rst_pdm_apb); ++ if (ret) { ++ dev_err(priv->dev, "failed to deassert pdm_apb\n"); ++ goto disable_pdm_apb; ++ } ++ ++ ret = clk_set_parent(priv->clk_mclk, priv->clk_mclk_ext); ++ if (ret) { ++ dev_err(priv->dev, "failed to set parent clk_mclk ret=%d\n", ret); ++ goto disable_pdm_apb; ++ } ++ ++ return 0; ++ ++disable_pdm_apb: ++ clk_disable_unprepare(priv->clk_pdm_apb); ++disable_pdm_mclk: ++ clk_disable_unprepare(priv->clk_pdm_mclk); ++ ++ return ret; ++} ++ ++#ifdef CONFIG_PM ++static int sf_pdm_runtime_suspend(struct device *dev) ++{ ++ struct sf_pdm *priv = dev_get_drvdata(dev); ++ ++ clk_disable_unprepare(priv->clk_pdm_apb); ++ clk_disable_unprepare(priv->clk_pdm_mclk); ++ ++ return 0; ++} ++ ++static int sf_pdm_runtime_resume(struct device *dev) ++{ ++ struct sf_pdm *priv = dev_get_drvdata(dev); ++ int ret; ++ ++ ret = sf_pdm_clock_enable(priv); ++ if (!ret) ++ sf_pdm_module_init(priv); ++ ++ return ret; ++} ++#endif ++ ++#ifdef CONFIG_PM_SLEEP ++static int sf_pdm_suspend(struct snd_soc_component *component) ++{ ++ return pm_runtime_force_suspend(component->dev); ++} ++ ++static int sf_pdm_resume(struct snd_soc_component *component) ++{ ++ return pm_runtime_force_resume(component->dev); ++} ++ ++#else ++#define sf_pdm_suspend NULL ++#define sf_pdm_resume NULL ++#endif ++ ++static const struct snd_soc_component_driver sf_pdm_component_drv = { ++ .name = "jh7110-pdm", ++ .probe = sf_pdm_component_probe, ++ .suspend = sf_pdm_suspend, ++ .resume = sf_pdm_resume, ++}; ++ ++static const struct regmap_config sf_pdm_regmap_cfg = { ++ .reg_bits = 32, ++ .val_bits = 32, ++ .reg_stride = 4, ++ .max_register = 0x20, ++}; ++ ++static int sf_pdm_clock_get(struct platform_device *pdev, struct sf_pdm *priv) ++{ ++ int ret; ++ ++ static struct clk_bulk_data clks[] = { ++ { .id = "pdm_mclk" }, ++ { .id = "pdm_apb" }, ++ { .id = "clk_mclk" }, ++ { .id = "mclk_ext" }, ++ }; ++ ++ ret = devm_clk_bulk_get(&pdev->dev, ARRAY_SIZE(clks), clks); ++ if (ret) { ++ dev_err(&pdev->dev, "failed to get pdm clocks\n"); ++ goto exit; ++ } ++ ++ priv->clk_pdm_mclk = clks[0].clk; ++ priv->clk_pdm_apb = clks[1].clk; ++ priv->clk_mclk = clks[2].clk; ++ priv->clk_mclk_ext = clks[3].clk; ++ ++ priv->rst_pdm_dmic = devm_reset_control_get_exclusive(&pdev->dev, "pdm_dmic"); ++ if (IS_ERR(priv->rst_pdm_dmic)) { ++ dev_err(&pdev->dev, "failed to get pdm_dmic reset control\n"); ++ ret = PTR_ERR(priv->rst_pdm_dmic); ++ goto exit; ++ } ++ ++ priv->rst_pdm_apb = devm_reset_control_get_exclusive(&pdev->dev, "pdm_apb"); ++ if (IS_ERR(priv->rst_pdm_apb)) { ++ dev_err(&pdev->dev, "failed to get pdm_apb reset control\n"); ++ ret = PTR_ERR(priv->rst_pdm_apb); ++ goto exit; ++ } ++ ++ /* ++ * pdm clock must always be enabled as hardware issue that ++ * no data in the first 4 seconds of the first recording ++ */ ++ ret = sf_pdm_clock_enable(priv); ++ ++exit: ++ return ret; ++} ++ ++static int sf_pdm_probe(struct platform_device *pdev) ++{ ++ struct sf_pdm *priv; ++ struct resource *res; ++ void __iomem *regs; ++ int ret; ++ ++ priv = devm_kzalloc(&pdev->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(&pdev->dev, regs, &sf_pdm_regmap_cfg); ++ if (IS_ERR(priv->pdm_map)) { ++ dev_err(&pdev->dev, "failed to init regmap: %ld\n", ++ PTR_ERR(priv->pdm_map)); ++ return PTR_ERR(priv->pdm_map); ++ } ++ ++ priv->dev = &pdev->dev; ++ priv->flag_first = 1; ++ ++ ret = sf_pdm_clock_get(pdev, priv); ++ if (ret) { ++ dev_err(&pdev->dev, "failed to enable audio-pdm clock\n"); ++ return ret; ++ } ++ ++ dev_set_drvdata(&pdev->dev, priv); ++ ++ ret = devm_snd_soc_register_component(&pdev->dev, &sf_pdm_component_drv, ++ &sf_pdm_dai_drv, 1); ++ if (ret) { ++ dev_err(&pdev->dev, "failed to register pdm dai\n"); ++ return ret; ++ } ++ pm_runtime_enable(&pdev->dev); ++ ++ return 0; ++} ++ ++static int sf_pdm_dev_remove(struct platform_device *pdev) ++{ ++ pm_runtime_disable(&pdev->dev); ++ return 0; ++} ++ ++static const struct of_device_id sf_pdm_of_match[] = { ++ {.compatible = "starfive,jh7110-pdm",}, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, sf_pdm_of_match); ++ ++static const struct dev_pm_ops sf_pdm_pm_ops = { ++ SET_RUNTIME_PM_OPS(sf_pdm_runtime_suspend, ++ sf_pdm_runtime_resume, NULL) ++}; ++ ++static struct platform_driver sf_pdm_driver = { ++ .driver = { ++ .name = "jh7110-pdm", ++ .of_match_table = sf_pdm_of_match, ++ .pm = &sf_pdm_pm_ops, ++ }, ++ .probe = sf_pdm_probe, ++ .remove = sf_pdm_dev_remove, ++}; ++module_platform_driver(sf_pdm_driver); ++ ++MODULE_AUTHOR("Walker Chen "); ++MODULE_DESCRIPTION("Starfive PDM Controller Driver"); ++MODULE_LICENSE("GPL v2"); diff --git a/target/linux/starfive/patches-6.6/0082-dt-binding-input-Add-tink_ft5406.patch b/target/linux/starfive/patches-6.6/0082-dt-binding-input-Add-tink_ft5406.patch new file mode 100644 index 0000000000..ed8a3af308 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0082-dt-binding-input-Add-tink_ft5406.patch @@ -0,0 +1,55 @@ +From 59cbdfeee0fc1ad382a0bc8f7fa897a9f5d03df0 Mon Sep 17 00:00:00 2001 +From: Changhuang Liang +Date: Fri, 9 Jun 2023 16:54:36 +0800 +Subject: [PATCH 082/116] dt-binding: input: Add tink_ft5406 + +Add tink_ft5406. + +Signed-off-by: Changhuang Liang +--- + .../bindings/input/tinker_ft5406.yaml | 39 +++++++++++++++++++ + 1 file changed, 39 insertions(+) + create mode 100644 Documentation/devicetree/bindings/input/tinker_ft5406.yaml + +--- /dev/null ++++ b/Documentation/devicetree/bindings/input/tinker_ft5406.yaml +@@ -0,0 +1,39 @@ ++# SPDX-License-Identifier: GPL-2.0 ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/input/touchscreen/goodix.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: Tinker FT5406 touchscreen controller Bindings ++ ++maintainers: ++ - Changhuang Liang ++ ++allOf: ++ - $ref: touchscreen.yaml# ++ ++properties: ++ compatible: ++ const: tinker_ft5406 ++ ++ reg: ++ const: 0x38 ++ ++additionalProperties: false ++ ++required: ++ - compatible ++ - reg ++ ++examples: ++ - | ++ i2c { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ tinker_ft5406@38 { ++ compatible = "tinker_ft5406"; ++ reg = <0x38>; ++ }; ++ }; ++ ++... diff --git a/target/linux/starfive/patches-6.6/0083-input-touchscreen-Add-tinker_ft5406-driver-support.patch b/target/linux/starfive/patches-6.6/0083-input-touchscreen-Add-tinker_ft5406-driver-support.patch new file mode 100644 index 0000000000..f45085f495 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0083-input-touchscreen-Add-tinker_ft5406-driver-support.patch @@ -0,0 +1,444 @@ +From 4800b6e0f2190d991cd4e5352167a9422841f195 Mon Sep 17 00:00:00 2001 +From: Changhuang Liang +Date: Wed, 21 Dec 2022 16:20:04 +0800 +Subject: [PATCH 083/116] input: touchscreen: Add tinker_ft5406 driver support + +Add tinker_ft5406 driver support + +Signed-off-by: Changhuang Liang +--- + drivers/input/touchscreen/Kconfig | 6 + + drivers/input/touchscreen/Makefile | 1 + + drivers/input/touchscreen/tinker_ft5406.c | 406 ++++++++++++++++++++++ + 3 files changed, 413 insertions(+) + create mode 100644 drivers/input/touchscreen/tinker_ft5406.c + +--- a/drivers/input/touchscreen/Kconfig ++++ b/drivers/input/touchscreen/Kconfig +@@ -1399,4 +1399,10 @@ config TOUCHSCREEN_HIMAX_HX83112B + To compile this driver as a module, choose M here: the + module will be called himax_hx83112b. + ++config TOUCHSCREEN_TINKER_FT5406 ++ tristate "tinker ft5406" ++ depends on I2C ++ help ++ Control ft5406 touch ic. ++ + endif +--- a/drivers/input/touchscreen/Makefile ++++ b/drivers/input/touchscreen/Makefile +@@ -118,3 +118,4 @@ obj-$(CONFIG_TOUCHSCREEN_IQS5XX) += iqs5 + obj-$(CONFIG_TOUCHSCREEN_IQS7211) += iqs7211.o + obj-$(CONFIG_TOUCHSCREEN_ZINITIX) += zinitix.o + obj-$(CONFIG_TOUCHSCREEN_HIMAX_HX83112B) += himax_hx83112b.o ++obj-$(CONFIG_TOUCHSCREEN_TINKER_FT5406) += tinker_ft5406.o +--- /dev/null ++++ b/drivers/input/touchscreen/tinker_ft5406.c +@@ -0,0 +1,406 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * ++ * TINKER BOARD FT5406 touch driver. ++ * ++ * Copyright (c) 2016 ASUSTek Computer Inc. ++ * Copyright (c) 2012-2014, The Linux Foundation. All rights reserved. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define RETRY_COUNT 10 ++#define FT_ONE_TCH_LEN 6 ++ ++#define FT_REG_FW_VER 0xA6 ++#define FT_REG_FW_MIN_VER 0xB2 ++#define FT_REG_FW_SUB_MIN_VER 0xB3 ++ ++#define VALID_TD_STATUS_VAL 10 ++#define MAX_TOUCH_POINTS 5 ++ ++#define FT_PRESS 0x7F ++#define FT_MAX_ID 0x0F ++ ++#define FT_TOUCH_X_H 0 ++#define FT_TOUCH_X_L 1 ++#define FT_TOUCH_Y_H 2 ++#define FT_TOUCH_Y_L 3 ++#define FT_TOUCH_EVENT 0 ++#define FT_TOUCH_ID 2 ++ ++#define FT_TOUCH_X_H_REG 3 ++#define FT_TOUCH_X_L_REG 4 ++#define FT_TOUCH_Y_H_REG 5 ++#define FT_TOUCH_Y_L_REG 6 ++#define FT_TD_STATUS_REG 2 ++#define FT_TOUCH_EVENT_REG 3 ++#define FT_TOUCH_ID_REG 5 ++ ++#define FT_TOUCH_DOWN 0 ++#define FT_TOUCH_CONTACT 2 ++ ++struct ts_event { ++ u16 au16_x[MAX_TOUCH_POINTS]; /*x coordinate */ ++ u16 au16_y[MAX_TOUCH_POINTS]; /*y coordinate */ ++ u8 au8_touch_event[MAX_TOUCH_POINTS]; /*touch event: 0:down; 1:up; 2:contact */ ++ u8 au8_finger_id[MAX_TOUCH_POINTS]; /*touch ID */ ++ u16 pressure; ++ u8 touch_point; ++ u8 point_num; ++}; ++ ++struct tinker_ft5406_data { ++ struct device *dev; ++ struct i2c_client *client; ++ struct input_dev *input_dev; ++ struct ts_event event; ++ struct work_struct ft5406_work; ++ ++ int screen_width; ++ int screen_height; ++ int xy_reverse; ++ int known_ids; ++ int retry_count; ++ bool finish_work; ++}; ++ ++struct tinker_ft5406_data *g_ts_data; ++ ++static int fts_i2c_read(struct i2c_client *client, char *writebuf, ++ int writelen, char *readbuf, int readlen) ++{ ++ int ret; ++ ++ if (writelen > 0) { ++ struct i2c_msg msgs[] = { ++ { ++ .addr = client->addr, ++ .flags = 0, ++ .len = writelen, ++ .buf = writebuf, ++ }, ++ { ++ .addr = client->addr, ++ .flags = I2C_M_RD, ++ .len = readlen, ++ .buf = readbuf, ++ }, ++ }; ++ ret = i2c_transfer(client->adapter, msgs, 2); ++ if (ret < 0) ++ dev_err(&client->dev, "i2c read error, %d\n", ret); ++ } else { ++ struct i2c_msg msgs[] = { ++ { ++ .addr = client->addr, ++ .flags = I2C_M_RD, ++ .len = readlen, ++ .buf = readbuf, ++ }, ++ }; ++ ret = i2c_transfer(client->adapter, msgs, 1); ++ if (ret < 0) ++ dev_err(&client->dev, "i2c read error, %d\n", ret); ++ } ++ ++ return ret; ++} ++ ++static int fts_read_reg(struct i2c_client *client, u8 addr, u8 *val) ++{ ++ return fts_i2c_read(client, &addr, 1, val, 1); ++} ++ ++static int fts_check_fw_ver(struct i2c_client *client) ++{ ++ u8 reg_addr, fw_ver[3]; ++ int ret; ++ ++ reg_addr = FT_REG_FW_VER; ++ ret = fts_i2c_read(client, ®_addr, 1, &fw_ver[0], 1); ++ if (ret < 0) ++ goto error; ++ ++ reg_addr = FT_REG_FW_MIN_VER; ++ ret = fts_i2c_read(client, ®_addr, 1, &fw_ver[1], 1); ++ if (ret < 0) ++ goto error; ++ ++ reg_addr = FT_REG_FW_SUB_MIN_VER; ++ ret = fts_i2c_read(client, ®_addr, 1, &fw_ver[2], 1); ++ if (ret < 0) ++ goto error; ++ ++ dev_info(&client->dev, "Firmware version = %d.%d.%d\n", ++ fw_ver[0], fw_ver[1], fw_ver[2]); ++ return 0; ++ ++error: ++ return ret; ++} ++ ++static int fts_read_td_status(struct tinker_ft5406_data *ts_data) ++{ ++ u8 td_status; ++ int ret = -1; ++ ++ ret = fts_read_reg(ts_data->client, FT_TD_STATUS_REG, &td_status); ++ if (ret < 0) { ++ dev_err(&ts_data->client->dev, ++ "Get reg td_status failed, %d\n", ret); ++ return ret; ++ } ++ return (int)td_status; ++} ++ ++static int fts_read_touchdata(struct tinker_ft5406_data *ts_data) ++{ ++ struct ts_event *event = &ts_data->event; ++ int ret = -1, i; ++ u8 buf[FT_ONE_TCH_LEN-2] = { 0 }; ++ u8 reg_addr, pointid = FT_MAX_ID; ++ ++ for (i = 0; i < event->touch_point && i < MAX_TOUCH_POINTS; i++) { ++ reg_addr = FT_TOUCH_X_H_REG + (i * FT_ONE_TCH_LEN); ++ ret = fts_i2c_read(ts_data->client, ®_addr, 1, buf, FT_ONE_TCH_LEN-2); ++ if (ret < 0) { ++ dev_err(&ts_data->client->dev, "Read touchdata failed.\n"); ++ return ret; ++ } ++ ++ pointid = (buf[FT_TOUCH_ID]) >> 4; ++ if (pointid >= MAX_TOUCH_POINTS) ++ break; ++ event->au8_finger_id[i] = pointid; ++ event->au16_x[i] = (s16) (buf[FT_TOUCH_X_H] & 0x0F) << 8 | (s16) buf[FT_TOUCH_X_L]; ++ event->au16_y[i] = (s16) (buf[FT_TOUCH_Y_H] & 0x0F) << 8 | (s16) buf[FT_TOUCH_Y_L]; ++ event->au8_touch_event[i] = buf[FT_TOUCH_EVENT] >> 6; ++ ++ if (ts_data->xy_reverse) { ++ event->au16_x[i] = ts_data->screen_width - event->au16_x[i] - 1; ++ event->au16_y[i] = ts_data->screen_height - event->au16_y[i] - 1; ++ } ++ } ++ event->pressure = FT_PRESS; ++ ++ return 0; ++} ++ ++static void fts_report_value(struct tinker_ft5406_data *ts_data) ++{ ++ struct ts_event *event = &ts_data->event; ++ int i, modified_ids = 0, released_ids; ++ ++ for (i = 0; i < event->touch_point && i < MAX_TOUCH_POINTS; i++) { ++ if (event->au8_touch_event[i] == FT_TOUCH_DOWN || ++ event->au8_touch_event[i] == FT_TOUCH_CONTACT) { ++ modified_ids |= 1 << event->au8_finger_id[i]; ++ input_mt_slot(ts_data->input_dev, event->au8_finger_id[i]); ++ input_mt_report_slot_state(ts_data->input_dev, MT_TOOL_FINGER, ++ true); ++ input_report_abs(ts_data->input_dev, ABS_MT_TOUCH_MAJOR, ++ event->pressure); ++ input_report_abs(ts_data->input_dev, ABS_MT_POSITION_X, ++ event->au16_x[i]); ++ input_report_abs(ts_data->input_dev, ABS_MT_POSITION_Y, ++ event->au16_y[i]); ++ ++ if (!((1 << event->au8_finger_id[i]) & ts_data->known_ids)) ++ dev_dbg(&ts_data->client->dev, "Touch id-%d: x = %d, y = %d\n", ++ event->au8_finger_id[i], ++ event->au16_x[i], ++ event->au16_y[i]); ++ } ++ } ++ ++ released_ids = ts_data->known_ids & ~modified_ids; ++ for (i = 0; released_ids && i < MAX_TOUCH_POINTS; i++) { ++ if (released_ids & (1<client->dev, "Release id-%d, known = %x modified = %x\n", ++ i, ts_data->known_ids, modified_ids); ++ input_mt_slot(ts_data->input_dev, i); ++ input_mt_report_slot_state(ts_data->input_dev, MT_TOOL_FINGER, false); ++ modified_ids &= ~(1 << i); ++ } ++ } ++ ts_data->known_ids = modified_ids; ++ input_mt_report_pointer_emulation(ts_data->input_dev, true); ++ input_sync(ts_data->input_dev); ++} ++ ++static void fts_retry_clear(struct tinker_ft5406_data *ts_data) ++{ ++ if (ts_data->retry_count != 0) ++ ts_data->retry_count = 0; ++} ++ ++static int fts_retry_wait(struct tinker_ft5406_data *ts_data) ++{ ++ if (ts_data->retry_count < RETRY_COUNT) { ++ dev_info(&ts_data->client->dev, ++ "Wait and retry, count = %d\n", ts_data->retry_count); ++ ts_data->retry_count++; ++ msleep(1000); ++ return 1; ++ } ++ dev_err(&ts_data->client->dev, "Attach retry count\n"); ++ return 0; ++} ++ ++static void tinker_ft5406_work(struct work_struct *work) ++{ ++ struct ts_event *event = &g_ts_data->event; ++ int ret = 0, td_status; ++ ++ /* polling 60fps */ ++ while (!g_ts_data->finish_work) { ++ td_status = fts_read_td_status(g_ts_data); ++ if (td_status < 0) { ++ ret = fts_retry_wait(g_ts_data); ++ if (ret == 0) { ++ dev_err(&g_ts_data->client->dev, "Stop touch polling\n"); ++ break; ++ } ++ } else if (td_status < VALID_TD_STATUS_VAL + 1 && ++ (td_status > 0 || g_ts_data->known_ids != 0)) { ++ fts_retry_clear(g_ts_data); ++ memset(event, -1, sizeof(struct ts_event)); ++ event->touch_point = td_status; ++ ret = fts_read_touchdata(g_ts_data); ++ if (ret == 0) ++ fts_report_value(g_ts_data); ++ } ++ msleep_interruptible(17); ++ } ++} ++ ++static int tinker_ft5406_open(struct input_dev *dev) ++{ ++ schedule_work(&g_ts_data->ft5406_work); ++ return 0; ++} ++ ++static void tinker_ft5406_close(struct input_dev *dev) ++{ ++ g_ts_data->finish_work = true; ++ cancel_work_sync(&g_ts_data->ft5406_work); ++ g_ts_data->finish_work = false; ++} ++ ++static int tinker_ft5406_probe(struct i2c_client *client) ++{ ++ struct input_dev *input_dev; ++ int ret = 0; ++ ++ dev_info(&client->dev, "Address = 0x%x\n", client->addr); ++ ++ g_ts_data = kzalloc(sizeof(struct tinker_ft5406_data), GFP_KERNEL); ++ if (g_ts_data == NULL) { ++ dev_err(&client->dev, "No memory for device\n"); ++ return -ENOMEM; ++ } ++ ++ g_ts_data->client = client; ++ i2c_set_clientdata(client, g_ts_data); ++ ++ g_ts_data->screen_width = 800; ++ g_ts_data->screen_height = 480; ++ g_ts_data->xy_reverse = 1; ++ ++ dev_info(&client->dev, "width = %d, height = %d, reverse = %d\n", ++ g_ts_data->screen_width, g_ts_data->screen_height, g_ts_data->xy_reverse); ++ ++ ret = fts_check_fw_ver(g_ts_data->client); ++ if (ret) { ++ dev_err(&client->dev, "Checking touch ic failed\n"); ++ goto check_fw_err; ++ } ++ ++ input_dev = input_allocate_device(); ++ if (!input_dev) { ++ dev_err(&client->dev, "Failed to allocate input device\n"); ++ goto input_allocate_failed; ++ } ++ input_dev->name = "fts_ts"; ++ input_dev->id.bustype = BUS_I2C; ++ input_dev->dev.parent = &g_ts_data->client->dev; ++ input_dev->open = tinker_ft5406_open; ++ input_dev->close = tinker_ft5406_close; ++ ++ g_ts_data->input_dev = input_dev; ++ input_set_drvdata(input_dev, g_ts_data); ++ ++ __set_bit(EV_SYN, input_dev->evbit); ++ __set_bit(EV_KEY, input_dev->evbit); ++ __set_bit(EV_ABS, input_dev->evbit); ++ __set_bit(BTN_TOUCH, input_dev->keybit); ++ ++ input_mt_init_slots(input_dev, MAX_TOUCH_POINTS, 0); ++ input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, g_ts_data->screen_width, 0, 0); ++ input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, g_ts_data->screen_height, 0, 0); ++ ++ ret = input_register_device(input_dev); ++ if (ret) { ++ dev_err(&client->dev, "Input device registration failed\n"); ++ goto input_register_failed; ++ } ++ ++ INIT_WORK(&g_ts_data->ft5406_work, tinker_ft5406_work); ++ ++ return 0; ++ ++input_register_failed: ++ input_free_device(input_dev); ++input_allocate_failed: ++check_fw_err: ++ kfree(g_ts_data); ++ g_ts_data = NULL; ++ return ret; ++} ++ ++static void tinker_ft5406_remove(struct i2c_client *client) ++{ ++ cancel_work_sync(&g_ts_data->ft5406_work); ++ if (g_ts_data->input_dev) { ++ input_unregister_device(g_ts_data->input_dev); ++ input_free_device(g_ts_data->input_dev); ++ } ++ kfree(g_ts_data); ++ g_ts_data = NULL; ++} ++ ++static const struct i2c_device_id tinker_ft5406_id[] = { ++ {"tinker_ft5406", 0}, ++ {}, ++}; ++ ++static struct i2c_driver tinker_ft5406_driver = { ++ .driver = { ++ .name = "tinker_ft5406", ++ }, ++ .probe = tinker_ft5406_probe, ++ .remove = tinker_ft5406_remove, ++ .id_table = tinker_ft5406_id, ++}; ++module_i2c_driver(tinker_ft5406_driver); ++ ++MODULE_DESCRIPTION("TINKER BOARD FT5406 Touch driver"); ++MODULE_LICENSE("GPL v2"); diff --git a/target/linux/starfive/patches-6.6/0084-dt-binding-media-Add-JH7110-Camera-Subsystem.patch b/target/linux/starfive/patches-6.6/0084-dt-binding-media-Add-JH7110-Camera-Subsystem.patch new file mode 100644 index 0000000000..69d7c7f494 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0084-dt-binding-media-Add-JH7110-Camera-Subsystem.patch @@ -0,0 +1,246 @@ +From b477a1a53553336edcfeb83be1b35817928daed8 Mon Sep 17 00:00:00 2001 +From: Changhuang Liang +Date: Mon, 5 Jun 2023 14:46:16 +0800 +Subject: [PATCH 084/116] dt-binding: media: Add JH7110 Camera Subsystem. + +Add the bindings documentation for Starfive JH7110 Camera Subsystem +which is used for handing image sensor data. + +Signed-off-by: Changhuang Liang +Signed-off-by: Jack Zhu +--- + .../bindings/media/starfive,jh7110-camss.yaml | 228 ++++++++++++++++++ + 1 file changed, 228 insertions(+) + create mode 100644 Documentation/devicetree/bindings/media/starfive,jh7110-camss.yaml + +--- /dev/null ++++ b/Documentation/devicetree/bindings/media/starfive,jh7110-camss.yaml +@@ -0,0 +1,228 @@ ++# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/media/starfive,jh7110-camss.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: Starfive SoC CAMSS ISP ++ ++maintainers: ++ - Jack Zhu ++ - Changhuang Liang ++ ++description: ++ The Starfive CAMSS ISP is a Camera interface for Starfive JH7110 SoC. It ++ consists of a VIN controller (Video In Controller, a top-level control unit) ++ and an ISP. ++ ++properties: ++ compatible: ++ const: starfive,jh7110-vin ++ ++ reg: ++ maxItems: 8 ++ ++ reg-names: ++ items: ++ - const: csi2rx ++ - const: vclk ++ - const: vrst ++ - const: sctrl ++ - const: isp ++ - const: trst ++ - const: pmu ++ - const: syscrg ++ ++ clocks: ++ maxItems: 16 ++ ++ clock-names: ++ items: ++ - const: clk_apb_func ++ - const: clk_pclk ++ - const: clk_sys_clk ++ - const: clk_wrapper_clk_c ++ - const: clk_dvp_inv ++ - const: clk_axiwr ++ - const: clk_mipi_rx0_pxl ++ - const: clk_pixel_clk_if0 ++ - const: clk_pixel_clk_if1 ++ - const: clk_pixel_clk_if2 ++ - const: clk_pixel_clk_if3 ++ - const: clk_m31dphy_cfgclk_in ++ - const: clk_m31dphy_refclk_in ++ - const: clk_m31dphy_txclkesc_lan0 ++ - const: clk_ispcore_2x ++ - const: clk_isp_axi ++ ++ resets: ++ maxItems: 14 ++ ++ reset-names: ++ items: ++ - const: rst_wrapper_p ++ - const: rst_wrapper_c ++ - const: rst_pclk ++ - const: rst_sys_clk ++ - const: rst_axird ++ - const: rst_axiwr ++ - const: rst_pixel_clk_if0 ++ - const: rst_pixel_clk_if1 ++ - const: rst_pixel_clk_if2 ++ - const: rst_pixel_clk_if3 ++ - const: rst_m31dphy_hw ++ - const: rst_m31dphy_b09_always_on ++ - const: rst_isp_top_n ++ - const: rst_isp_top_axi ++ ++ power-domains: ++ items: ++ - description: JH7110 ISP Power Domain Switch Controller. ++ ++ interrupts: ++ maxItems: 5 ++ ++ ports: ++ $ref: /schemas/graph.yaml#/properties/ports ++ ++ properties: ++ port@0: ++ $ref: /schemas/graph.yaml#/$defs/port-base ++ unevaluatedProperties: false ++ description: Input port for receiving DVP data. ++ ++ properties: ++ endpoint: ++ $ref: video-interfaces.yaml# ++ unevaluatedProperties: false ++ ++ properties: ++ bus-type: ++ enum: [5, 6] ++ ++ bus-width: ++ enum: [8, 10, 12] ++ ++ data-shift: ++ enum: [0, 2] ++ default: 0 ++ ++ hsync-active: ++ enum: [0, 1] ++ default: 1 ++ ++ vsync-active: ++ enum: [0, 1] ++ default: 1 ++ ++ required: ++ - bus-type ++ - bus-width ++ ++ port@1: ++ $ref: /schemas/graph.yaml#/properties/port ++ description: Input port for receiving CSI data. ++ ++ required: ++ - port@0 ++ - port@1 ++ ++required: ++ - compatible ++ - reg ++ - reg-names ++ - clocks ++ - clock-names ++ - resets ++ - reset-names ++ - power-domains ++ - interrupts ++ - ports ++ ++additionalProperties: false ++ ++examples: ++ - | ++ vin_sysctl: vin_sysctl@19800000 { ++ compatible = "starfive,jh7110-vin"; ++ reg = <0x0 0x19800000 0x0 0x10000>, ++ <0x0 0x19810000 0x0 0x10000>, ++ <0x0 0x19820000 0x0 0x10000>, ++ <0x0 0x19840000 0x0 0x10000>, ++ <0x0 0x19870000 0x0 0x30000>, ++ <0x0 0x11840000 0x0 0x10000>, ++ <0x0 0x17030000 0x0 0x10000>, ++ <0x0 0x13020000 0x0 0x10000>; ++ reg-names = "csi2rx", "vclk", "vrst", "sctrl", ++ "isp", "trst", "pmu", "syscrg"; ++ clocks = <&clkisp JH7110_DOM4_APB_FUNC>, ++ <&clkisp JH7110_U0_VIN_PCLK>, ++ <&clkisp JH7110_U0_VIN_SYS_CLK>, ++ <&clkisp JH7110_U0_ISPV2_TOP_WRAPPER_CLK_C>, ++ <&clkisp JH7110_DVP_INV>, ++ <&clkisp JH7110_U0_VIN_CLK_P_AXIWR>, ++ <&clkisp JH7110_MIPI_RX0_PXL>, ++ <&clkisp JH7110_U0_VIN_PIXEL_CLK_IF0>, ++ <&clkisp JH7110_U0_VIN_PIXEL_CLK_IF1>, ++ <&clkisp JH7110_U0_VIN_PIXEL_CLK_IF2>, ++ <&clkisp JH7110_U0_VIN_PIXEL_CLK_IF3>, ++ <&clkisp JH7110_U0_M31DPHY_CFGCLK_IN>, ++ <&clkisp JH7110_U0_M31DPHY_REFCLK_IN>, ++ <&clkisp JH7110_U0_M31DPHY_TXCLKESC_LAN0>, ++ <&clkgen JH7110_ISP_TOP_CLK_ISPCORE_2X>, ++ <&clkgen JH7110_ISP_TOP_CLK_ISP_AXI>; ++ clock-names = "clk_apb_func", "clk_pclk", "clk_sys_clk", ++ "clk_wrapper_clk_c", "clk_dvp_inv", "clk_axiwr", ++ "clk_mipi_rx0_pxl", "clk_pixel_clk_if0", ++ "clk_pixel_clk_if1", "clk_pixel_clk_if2", ++ "clk_pixel_clk_if3", "clk_m31dphy_cfgclk_in", ++ "clk_m31dphy_refclk_in", "clk_m31dphy_txclkesc_lan0", ++ "clk_ispcore_2x", "clk_isp_axi"; ++ resets = <&rstgen RSTN_U0_ISPV2_TOP_WRAPPER_P>, ++ <&rstgen RSTN_U0_ISPV2_TOP_WRAPPER_C>, ++ <&rstgen RSTN_U0_VIN_N_PCLK>, ++ <&rstgen RSTN_U0_VIN_N_SYS_CLK>, ++ <&rstgen RSTN_U0_VIN_P_AXIRD>, ++ <&rstgen RSTN_U0_VIN_P_AXIWR>, ++ <&rstgen RSTN_U0_VIN_N_PIXEL_CLK_IF0>, ++ <&rstgen RSTN_U0_VIN_N_PIXEL_CLK_IF1>, ++ <&rstgen RSTN_U0_VIN_N_PIXEL_CLK_IF2>, ++ <&rstgen RSTN_U0_VIN_N_PIXEL_CLK_IF3>, ++ <&rstgen RSTN_U0_M31DPHY_HW>, ++ <&rstgen RSTN_U0_M31DPHY_B09_ALWAYS_ON>, ++ <&rstgen RSTN_U0_DOM_ISP_TOP_N>, ++ <&rstgen RSTN_U0_DOM_ISP_TOP_AXI>; ++ reset-names = "rst_wrapper_p", "rst_wrapper_c", "rst_pclk", ++ "rst_sys_clk", "rst_axird", "rst_axiwr", "rst_pixel_clk_if0", ++ "rst_pixel_clk_if1", "rst_pixel_clk_if2", "rst_pixel_clk_if3", ++ "rst_m31dphy_hw", "rst_m31dphy_b09_always_on", ++ "rst_isp_top_n", "rst_isp_top_axi"; ++ starfive,aon-syscon = <&aon_syscon 0x00>; ++ power-domains = <&pwrc JH7110_PD_ISP>; ++ /* irq nr: vin, isp, isp_csi, isp_scd, isp_csiline */ ++ interrupts = <92>, <87>, <88>, <89>, <90>; ++ ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ port@0 { ++ reg = <0>; ++ vin_from_sc2235: endpoint { ++ remote-endpoint = <&sc2235_to_vin>; ++ bus-type = <5>; ++ bus-width = <8>; ++ data-shift = <2>; ++ hsync-active = <1>; ++ vsync-active = <0>; ++ pclk-sample = <1>; ++ }; ++ }; ++ ++ port@1 { ++ reg = <1>; ++ vin_from_csi2rx: endpoint { ++ remote-endpoint = <&csi2rx_to_vin>; ++ }; ++ }; ++ }; ++ }; diff --git a/target/linux/starfive/patches-6.6/0085-media-starfive-Add-vin-driver-support.patch b/target/linux/starfive/patches-6.6/0085-media-starfive-Add-vin-driver-support.patch new file mode 100644 index 0000000000..b5d5f73f9d --- /dev/null +++ b/target/linux/starfive/patches-6.6/0085-media-starfive-Add-vin-driver-support.patch @@ -0,0 +1,24014 @@ +From 908b10ebc95eb29caae8c4737b23a29af5c6298f Mon Sep 17 00:00:00 2001 +From: Changhuang Liang +Date: Mon, 5 Jun 2023 13:54:16 +0800 +Subject: [PATCH 085/116] media: starfive: Add vin driver support + +Add vin driver support. + +Signed-off-by: Changhuang Liang +--- + drivers/media/platform/Kconfig | 1 + + drivers/media/platform/Makefile | 1 + + drivers/media/platform/starfive/Kconfig | 56 + + drivers/media/platform/starfive/Makefile | 24 + + .../platform/starfive/v4l2_driver/Readme.txt | 11 + + .../starfive/v4l2_driver/imx219_mipi.c | 1583 ++++++++ + .../starfive/v4l2_driver/ov13850_mipi.c | 1921 ++++++++++ + .../starfive/v4l2_driver/ov4689_mipi.c | 2975 +++++++++++++++ + .../platform/starfive/v4l2_driver/ov5640.c | 3227 +++++++++++++++++ + .../platform/starfive/v4l2_driver/sc2235.c | 1914 ++++++++++ + .../starfive/v4l2_driver/stf_common.h | 185 + + .../platform/starfive/v4l2_driver/stf_csi.c | 465 +++ + .../platform/starfive/v4l2_driver/stf_csi.h | 61 + + .../starfive/v4l2_driver/stf_csi_hw_ops.c | 310 ++ + .../starfive/v4l2_driver/stf_csiphy.c | 357 ++ + .../starfive/v4l2_driver/stf_csiphy.h | 188 + + .../starfive/v4l2_driver/stf_csiphy_hw_ops.c | 335 ++ + .../starfive/v4l2_driver/stf_dmabuf.c | 123 + + .../starfive/v4l2_driver/stf_dmabuf.h | 12 + + .../platform/starfive/v4l2_driver/stf_dvp.c | 385 ++ + .../platform/starfive/v4l2_driver/stf_dvp.h | 67 + + .../starfive/v4l2_driver/stf_dvp_hw_ops.c | 187 + + .../platform/starfive/v4l2_driver/stf_event.c | 36 + + .../platform/starfive/v4l2_driver/stf_isp.c | 1521 ++++++++ + .../platform/starfive/v4l2_driver/stf_isp.h | 222 ++ + .../starfive/v4l2_driver/stf_isp_hw_ops.c | 1550 ++++++++ + .../starfive/v4l2_driver/stf_isp_ioctl.h | 133 + + .../platform/starfive/v4l2_driver/stf_video.c | 1552 ++++++++ + .../platform/starfive/v4l2_driver/stf_video.h | 83 + + .../platform/starfive/v4l2_driver/stf_vin.c | 1515 ++++++++ + .../platform/starfive/v4l2_driver/stf_vin.h | 182 + + .../starfive/v4l2_driver/stf_vin_hw_ops.c | 433 +++ + .../platform/starfive/v4l2_driver/stfcamss.c | 1369 +++++++ + .../platform/starfive/v4l2_driver/stfcamss.h | 117 + + include/uapi/linux/jh7110-isp.h | 253 ++ + include/uapi/linux/v4l2-controls.h | 6 + + include/video/stf-vin.h | 443 +++ + 37 files changed, 23803 insertions(+) + create mode 100644 drivers/media/platform/starfive/Kconfig + create mode 100644 drivers/media/platform/starfive/Makefile + create mode 100644 drivers/media/platform/starfive/v4l2_driver/Readme.txt + create mode 100644 drivers/media/platform/starfive/v4l2_driver/imx219_mipi.c + create mode 100644 drivers/media/platform/starfive/v4l2_driver/ov13850_mipi.c + create mode 100644 drivers/media/platform/starfive/v4l2_driver/ov4689_mipi.c + create mode 100644 drivers/media/platform/starfive/v4l2_driver/ov5640.c + create mode 100644 drivers/media/platform/starfive/v4l2_driver/sc2235.c + create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_common.h + create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_csi.c + create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_csi.h + create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_csi_hw_ops.c + create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_csiphy.c + create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_csiphy.h + create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_csiphy_hw_ops.c + create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_dmabuf.c + create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_dmabuf.h + create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_dvp.c + create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_dvp.h + create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_dvp_hw_ops.c + create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_event.c + create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_isp.c + create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_isp.h + create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_isp_hw_ops.c + create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_isp_ioctl.h + create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_video.c + create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_video.h + create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_vin.c + create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_vin.h + create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_vin_hw_ops.c + create mode 100644 drivers/media/platform/starfive/v4l2_driver/stfcamss.c + create mode 100644 drivers/media/platform/starfive/v4l2_driver/stfcamss.h + create mode 100644 include/uapi/linux/jh7110-isp.h + create mode 100644 include/video/stf-vin.h + +--- a/drivers/media/platform/Kconfig ++++ b/drivers/media/platform/Kconfig +@@ -80,6 +80,7 @@ source "drivers/media/platform/renesas/K + source "drivers/media/platform/rockchip/Kconfig" + source "drivers/media/platform/samsung/Kconfig" + source "drivers/media/platform/st/Kconfig" ++source "drivers/media/platform/starfive/Kconfig" + source "drivers/media/platform/sunxi/Kconfig" + source "drivers/media/platform/ti/Kconfig" + source "drivers/media/platform/verisilicon/Kconfig" +--- a/drivers/media/platform/Makefile ++++ b/drivers/media/platform/Makefile +@@ -23,6 +23,7 @@ obj-y += renesas/ + obj-y += rockchip/ + obj-y += samsung/ + obj-y += st/ ++obj-y += starfive/ + obj-y += sunxi/ + obj-y += ti/ + obj-y += verisilicon/ +--- /dev/null ++++ b/drivers/media/platform/starfive/Kconfig +@@ -0,0 +1,56 @@ ++# SPDX-License-Identifier: GPL-2.0-only ++ ++comment "Starfive media platform drivers" ++ ++config VIN_SENSOR_OV5640 ++ tristate "VIN SENSOR support OV5640" ++ depends on VIDEO_STF_VIN ++ select V4L2_FWNODE ++ default n ++ help ++ Say Y here if you want to have support for VIN sensor OV5640 ++ ++config VIN_SENSOR_SC2235 ++ tristate "VIN SENSOR support SC2235" ++ depends on VIDEO_STF_VIN ++ select V4L2_FWNODE ++ default n ++ help ++ Say Y here if you want to have support for VIN sensor SC2235 ++ ++config VIN_SENSOR_OV4689 ++ tristate "VIN SENSOR support OV4689" ++ depends on VIDEO_STF_VIN ++ select V4L2_FWNODE ++ default n ++ ++ help ++ Say Y here if you want to have support for VIN sensor OV4689 ++ ++config VIN_SENSOR_OV13850 ++ bool "VIN SENSOR support OV13850" ++ depends on VIDEO_STF_VIN ++ select V4L2_FWNODE ++ default n ++ help ++ Say Y here if you want to have support for VIN sensor OV13850 ++ ++config VIN_SENSOR_IMX219 ++ tristate "VIN SENSOR support IMX219" ++ depends on VIDEO_STF_VIN ++ select V4L2_FWNODE ++ default n ++ help ++ Say Y here if you want to have support for VIN sensor IMX219 ++ ++config VIDEO_STF_VIN ++ tristate "starfive VIC video in support" ++ depends on V4L_PLATFORM_DRIVERS ++ depends on VIDEO_DEV ++ select MEDIA_CONTROLLER ++ select VIDEOBUF2_DMA_CONTIG ++ select VIDEO_V4L2_SUBDEV_API ++ select V4L2_FWNODE ++ help ++ To compile this driver as a module, choose M here: the module ++ will be called stf-vin. +--- /dev/null ++++ b/drivers/media/platform/starfive/Makefile +@@ -0,0 +1,24 @@ ++# SPDX-License-Identifier: GPL-2.0 ++ ++obj-$(CONFIG_VIN_SENSOR_OV5640) += v4l2_driver/ov5640.o ++obj-$(CONFIG_VIN_SENSOR_SC2235) += v4l2_driver/sc2235.o ++obj-$(CONFIG_VIN_SENSOR_OV4689) += v4l2_driver/ov4689_mipi.o ++obj-$(CONFIG_VIN_SENSOR_OV13850) += v4l2_driver/ov13850_mipi.o ++obj-$(CONFIG_VIN_SENSOR_IMX219) += v4l2_driver/imx219_mipi.o ++ ++starfivecamss-objs += v4l2_driver/stfcamss.o \ ++ v4l2_driver/stf_event.o \ ++ v4l2_driver/stf_dvp.o \ ++ v4l2_driver/stf_csi.o \ ++ v4l2_driver/stf_csiphy.o \ ++ v4l2_driver/stf_isp.o \ ++ v4l2_driver/stf_video.o \ ++ v4l2_driver/stf_vin.o \ ++ v4l2_driver/stf_vin_hw_ops.o \ ++ v4l2_driver/stf_csi_hw_ops.o \ ++ v4l2_driver/stf_csiphy_hw_ops.o \ ++ v4l2_driver/stf_isp_hw_ops.o \ ++ v4l2_driver/stf_dvp_hw_ops.o \ ++ v4l2_driver/stf_dmabuf.o ++ ++obj-$(CONFIG_VIDEO_STF_VIN) += starfivecamss.o \ +--- /dev/null ++++ b/drivers/media/platform/starfive/v4l2_driver/Readme.txt +@@ -0,0 +1,11 @@ ++ ++/dev/video0: Output the camera data directly. ++/dev/video1: Output the data of the camera converted by isp. ++ ++ensure linux/arch/riscv/configs/starfive_jh7110_defconfig: ++CONFIG_VIDEO_STF_VIN=y ++CONFIG_VIN_SENSOR_SC2235=y ++CONFIG_VIN_SENSOR_OV4689=y ++ ++Only support the lane0/lane5 of dphy as clock lane, lane1/lane2/lane3/lane4 ++as data lane. +--- /dev/null ++++ b/drivers/media/platform/starfive/v4l2_driver/imx219_mipi.c +@@ -0,0 +1,1583 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * A V4L2 driver for Sony IMX219 cameras. ++ * Copyright (C) 2019, Raspberry Pi (Trading) Ltd ++ * ++ * Based on Sony imx258 camera driver ++ * Copyright (C) 2018 Intel Corporation ++ * ++ * DT / fwnode changes, and regulator / GPIO control taken from imx214 driver ++ * Copyright 2018 Qtechnology A/S ++ * ++ * Flip handling taken from the Sony IMX319 driver. ++ * Copyright (C) 2018 Intel Corporation ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define IMX219_REG_VALUE_08BIT 1 ++#define IMX219_REG_VALUE_16BIT 2 ++ ++#define IMX219_REG_MODE_SELECT 0x0100 ++#define IMX219_MODE_STANDBY 0x00 ++#define IMX219_MODE_STREAMING 0x01 ++ ++/* Chip ID */ ++#define IMX219_REG_CHIP_ID 0x0000 ++#define IMX219_CHIP_ID 0x0219 ++ ++/* External clock frequency is 24.0M */ ++#define IMX219_XCLK_FREQ 24000000 ++ ++/* Pixel rate is fixed at 182.4M for all the modes */ ++#define IMX219_PIXEL_RATE 182400000 ++ ++#define IMX219_DEFAULT_LINK_FREQ 456000000 ++ ++/* V_TIMING internal */ ++#define IMX219_REG_VTS 0x0160 ++#define IMX219_VTS_15FPS 0x0dc6 ++#define IMX219_VTS_30FPS_1080P 0x06e3 ++#define IMX219_VTS_30FPS_BINNED 0x06e3 ++#define IMX219_VTS_30FPS_1280x720 0x06e3 ++#define IMX219_VTS_30FPS_640x480 0x06e3 ++#define IMX219_VTS_MAX 0xffff ++ ++#define IMX219_VBLANK_MIN 4 ++ ++/*Frame Length Line*/ ++#define IMX219_FLL_MIN 0x08a6 ++#define IMX219_FLL_MAX 0xffff ++#define IMX219_FLL_STEP 1 ++#define IMX219_FLL_DEFAULT 0x0c98 ++ ++/* HBLANK control - read only */ ++#define IMX219_PPL_DEFAULT 3448 ++ ++/* Exposure control */ ++#define IMX219_REG_EXPOSURE 0x015a ++#define IMX219_EXPOSURE_MIN 4 ++#define IMX219_EXPOSURE_STEP 1 ++#define IMX219_EXPOSURE_DEFAULT 0x640 ++#define IMX219_EXPOSURE_MAX 65535 ++ ++/* Analog gain control */ ++#define IMX219_REG_ANALOG_GAIN 0x0157 ++#define IMX219_ANA_GAIN_MIN 0 ++#define IMX219_ANA_GAIN_MAX 232 ++#define IMX219_ANA_GAIN_STEP 1 ++#define IMX219_ANA_GAIN_DEFAULT 0xd0 ++ ++/* Digital gain control */ ++#define IMX219_REG_DIGITAL_GAIN 0x0158 ++#define IMX219_DGTL_GAIN_MIN 0x0100 ++#define IMX219_DGTL_GAIN_MAX 0x0fff ++#define IMX219_DGTL_GAIN_DEFAULT 0x0100 ++#define IMX219_DGTL_GAIN_STEP 1 ++ ++#define IMX219_REG_ORIENTATION 0x0172 ++ ++/* Test Pattern Control */ ++#define IMX219_REG_TEST_PATTERN 0x0600 ++#define IMX219_TEST_PATTERN_DISABLE 0 ++#define IMX219_TEST_PATTERN_SOLID_COLOR 1 ++#define IMX219_TEST_PATTERN_COLOR_BARS 2 ++#define IMX219_TEST_PATTERN_GREY_COLOR 3 ++#define IMX219_TEST_PATTERN_PN9 4 ++ ++/* Test pattern colour components */ ++#define IMX219_REG_TESTP_RED 0x0602 ++#define IMX219_REG_TESTP_GREENR 0x0604 ++#define IMX219_REG_TESTP_BLUE 0x0606 ++#define IMX219_REG_TESTP_GREENB 0x0608 ++#define IMX219_TESTP_COLOUR_MIN 0 ++#define IMX219_TESTP_COLOUR_MAX 0x03ff ++#define IMX219_TESTP_COLOUR_STEP 1 ++#define IMX219_TESTP_RED_DEFAULT IMX219_TESTP_COLOUR_MAX ++#define IMX219_TESTP_GREENR_DEFAULT 0 ++#define IMX219_TESTP_BLUE_DEFAULT 0 ++#define IMX219_TESTP_GREENB_DEFAULT 0 ++ ++/* IMX219 native and active pixel array size. */ ++#define IMX219_NATIVE_WIDTH 3296U ++#define IMX219_NATIVE_HEIGHT 2480U ++#define IMX219_PIXEL_ARRAY_LEFT 8U ++#define IMX219_PIXEL_ARRAY_TOP 8U ++#define IMX219_PIXEL_ARRAY_WIDTH 3280U ++#define IMX219_PIXEL_ARRAY_HEIGHT 2464U ++ ++struct imx219_reg { ++ u16 address; ++ u8 val; ++}; ++ ++struct imx219_reg_list { ++ unsigned int num_of_regs; ++ const struct imx219_reg *regs; ++}; ++ ++/* Mode : resolution and related config&values */ ++struct imx219_mode { ++ /* Frame width */ ++ unsigned int width; ++ /* Frame height */ ++ unsigned int height; ++ ++ unsigned int fps; ++ ++ /* Analog crop rectangle. */ ++ struct v4l2_rect crop; ++ ++ /* V-timing */ ++ unsigned int vts_def; ++ ++ /* Default register values */ ++ struct imx219_reg_list reg_list; ++}; ++ ++/* ++ * Register sets lifted off the i2C interface from the Raspberry Pi firmware ++ * driver. ++ * 3280x2464 = mode 2, 1920x1080 = mode 1, 1640x1232 = mode 4, 640x480 = mode 7. ++ */ ++ ++static const struct imx219_reg mode_1920_1080_regs[] = { ++ {0x0100, 0x00}, ++ {0x30eb, 0x05}, ++ {0x30eb, 0x0c}, ++ {0x300a, 0xff}, ++ {0x300b, 0xff}, ++ {0x30eb, 0x05}, ++ {0x30eb, 0x09}, ++ {0x0114, 0x01}, ++ {0x0128, 0x00}, ++ {0x012a, 0x18}, ++ {0x012b, 0x00}, ++ {0x0162, 0x0d}, ++ {0x0163, 0x78}, ++ {0x0164, 0x02}, ++ {0x0165, 0xa8}, ++ {0x0166, 0x0a}, ++ {0x0167, 0x27}, ++ {0x0168, 0x02}, ++ {0x0169, 0xb4}, ++ {0x016a, 0x06}, ++ {0x016b, 0xeb}, ++ {0x016c, 0x07}, ++ {0x016d, 0x80}, ++ {0x016e, 0x04}, ++ {0x016f, 0x38}, ++ {0x0170, 0x01}, ++ {0x0171, 0x01}, ++ {0x0174, 0x00}, ++ {0x0175, 0x00}, ++ {0x0301, 0x05}, ++ {0x0303, 0x01}, ++ {0x0304, 0x03}, ++ {0x0305, 0x03}, ++ {0x0306, 0x00}, ++ {0x0307, 0x39}, ++ {0x030b, 0x01}, ++ {0x030c, 0x00}, ++ {0x030d, 0x72}, ++ {0x0624, 0x07}, ++ {0x0625, 0x80}, ++ {0x0626, 0x04}, ++ {0x0627, 0x38}, ++ {0x455e, 0x00}, ++ {0x471e, 0x4b}, ++ {0x4767, 0x0f}, ++ {0x4750, 0x14}, ++ {0x4540, 0x00}, ++ {0x47b4, 0x14}, ++ {0x4713, 0x30}, ++ {0x478b, 0x10}, ++ {0x478f, 0x10}, ++ {0x4793, 0x10}, ++ {0x4797, 0x0e}, ++ {0x479b, 0x0e}, ++}; ++ ++static const struct imx219_reg mode_1280_720_regs[] = { ++ {0x0100, 0x00}, ++ {0x30eb, 0x05}, ++ {0x30eb, 0x0c}, ++ {0x300a, 0xff}, ++ {0x300b, 0xff}, ++ {0x30eb, 0x05}, ++ {0x30eb, 0x09}, ++ {0x0114, 0x01}, ++ {0x0128, 0x00}, ++ {0x012a, 0x18}, ++ {0x012b, 0x00}, ++ {0x0162, 0x0d}, ++ {0x0163, 0x78}, ++ {0x0164, 0x01}, ++ {0x0165, 0x68}, ++ {0x0166, 0x0b}, ++ {0x0167, 0x67}, ++ {0x0168, 0x02}, ++ {0x0169, 0x00}, ++ {0x016a, 0x07}, ++ {0x016b, 0x9f}, ++ {0x016c, 0x05}, ++ {0x016d, 0x00}, ++ {0x016e, 0x02}, ++ {0x016f, 0xd0}, ++ {0x0170, 0x01}, ++ {0x0171, 0x01}, ++ {0x0174, 0x01}, ++ {0x0175, 0x01}, ++ {0x0301, 0x05}, ++ {0x0303, 0x01}, ++ {0x0304, 0x03}, ++ {0x0305, 0x03}, ++ {0x0306, 0x00}, ++ {0x0307, 0x39}, ++ {0x030b, 0x01}, ++ {0x030c, 0x00}, ++ {0x030d, 0x72}, ++ {0x0624, 0x06}, ++ {0x0625, 0x68}, ++ {0x0626, 0x04}, ++ {0x0627, 0xd0}, ++ {0x455e, 0x00}, ++ {0x471e, 0x4b}, ++ {0x4767, 0x0f}, ++ {0x4750, 0x14}, ++ {0x4540, 0x00}, ++ {0x47b4, 0x14}, ++ {0x4713, 0x30}, ++ {0x478b, 0x10}, ++ {0x478f, 0x10}, ++ {0x4793, 0x10}, ++ {0x4797, 0x0e}, ++ {0x479b, 0x0e}, ++}; ++ ++static const struct imx219_reg mode_640_480_regs[] = { ++ {0x0100, 0x00}, ++ {0x30eb, 0x05}, ++ {0x30eb, 0x0c}, ++ {0x300a, 0xff}, ++ {0x300b, 0xff}, ++ {0x30eb, 0x05}, ++ {0x30eb, 0x09}, ++ {0x0114, 0x01}, ++ {0x0128, 0x00}, ++ {0x012a, 0x18}, ++ {0x012b, 0x00}, ++ {0x0162, 0x0d}, ++ {0x0163, 0x78}, ++ {0x0164, 0x03}, ++ {0x0165, 0xe8}, ++ {0x0166, 0x08}, ++ {0x0167, 0xe7}, ++ {0x0168, 0x02}, ++ {0x0169, 0xf0}, ++ {0x016a, 0x06}, ++ {0x016b, 0xaf}, ++ {0x016c, 0x02}, ++ {0x016d, 0x80}, ++ {0x016e, 0x01}, ++ {0x016f, 0xe0}, ++ {0x0170, 0x01}, ++ {0x0171, 0x01}, ++ {0x0174, 0x03}, ++ {0x0175, 0x03}, ++ {0x0301, 0x05}, ++ {0x0303, 0x01}, ++ {0x0304, 0x03}, ++ {0x0305, 0x03}, ++ {0x0306, 0x00}, ++ {0x0307, 0x39}, ++ {0x030b, 0x01}, ++ {0x030c, 0x00}, ++ {0x030d, 0x72}, ++ {0x0624, 0x06}, ++ {0x0625, 0x68}, ++ {0x0626, 0x04}, ++ {0x0627, 0xd0}, ++ {0x455e, 0x00}, ++ {0x471e, 0x4b}, ++ {0x4767, 0x0f}, ++ {0x4750, 0x14}, ++ {0x4540, 0x00}, ++ {0x47b4, 0x14}, ++ {0x4713, 0x30}, ++ {0x478b, 0x10}, ++ {0x478f, 0x10}, ++ {0x4793, 0x10}, ++ {0x4797, 0x0e}, ++ {0x479b, 0x0e}, ++}; ++ ++static const struct imx219_reg raw8_framefmt_regs[] = { ++ {0x018c, 0x08}, ++ {0x018d, 0x08}, ++ {0x0309, 0x08}, ++}; ++ ++static const struct imx219_reg raw10_framefmt_regs[] = { ++ {0x018c, 0x0a}, ++ {0x018d, 0x0a}, ++ {0x0309, 0x0a}, ++}; ++ ++static const s64 imx219_link_freq_menu[] = { ++ IMX219_DEFAULT_LINK_FREQ, ++}; ++ ++static const char * const imx219_test_pattern_menu[] = { ++ "Disabled", ++ "Color Bars", ++ "Solid Color", ++ "Grey Color Bars", ++ "PN9" ++}; ++ ++static const int imx219_test_pattern_val[] = { ++ IMX219_TEST_PATTERN_DISABLE, ++ IMX219_TEST_PATTERN_COLOR_BARS, ++ IMX219_TEST_PATTERN_SOLID_COLOR, ++ IMX219_TEST_PATTERN_GREY_COLOR, ++ IMX219_TEST_PATTERN_PN9, ++}; ++ ++/* regulator supplies */ ++static const char * const imx219_supply_name[] = { ++ /* Supplies can be enabled in any order */ ++ "VANA", /* Analog (2.8V) supply */ ++ "VDIG", /* Digital Core (1.8V) supply */ ++ "VDDL", /* IF (1.2V) supply */ ++}; ++ ++#define IMX219_NUM_SUPPLIES ARRAY_SIZE(imx219_supply_name) ++ ++/* ++ * The supported formats. ++ * This table MUST contain 4 entries per format, to cover the various flip ++ * combinations in the order ++ * - no flip ++ * - h flip ++ * - v flip ++ * - h&v flips ++ */ ++static const u32 codes[] = { ++ MEDIA_BUS_FMT_SRGGB10_1X10, ++ MEDIA_BUS_FMT_SGRBG10_1X10, ++ MEDIA_BUS_FMT_SGBRG10_1X10, ++ MEDIA_BUS_FMT_SBGGR10_1X10, ++ ++ MEDIA_BUS_FMT_SRGGB8_1X8, ++ MEDIA_BUS_FMT_SGRBG8_1X8, ++ MEDIA_BUS_FMT_SGBRG8_1X8, ++ MEDIA_BUS_FMT_SBGGR8_1X8, ++}; ++ ++/* ++ * Initialisation delay between XCLR low->high and the moment when the sensor ++ * can start capture (i.e. can leave software stanby) must be not less than: ++ * t4 + max(t5, t6 +