From a462d2b75781370c8778a36693a949614468d503 Mon Sep 17 00:00:00 2001 From: HunZI <277498654@qq.com> Date: Thu, 18 Jan 2024 15:16:24 +0800 Subject: [PATCH] target: add phytium support (#11798) * target: add phytium support * kernel/video: add phytium platform ARM GPU support * config: add EFI support to phytium armv8 * target: phytium: remove rtl8821cs driver * target: phytium: refresh dts --- config/Config-images.in | 3 +- package/boot/grub2/Makefile | 1 + package/kernel/linux/modules/video.mk | 6 +- target/linux/phytium/Makefile | 24 + target/linux/phytium/README | 5 + target/linux/phytium/armv8/config-5.10 | 827 +++ target/linux/phytium/armv8/target.mk | 11 + target/linux/phytium/base-files.mk | 8 + .../phytium/base-files/etc/board.d/01_led | 19 + .../phytium/base-files/etc/board.d/02_network | 18 + .../base-files/etc/board.d/03_gpio_switches | 23 + target/linux/phytium/base-files/etc/inittab | 9 + .../base-files/lib/preinit/01_sysinfo_acpi | 52 + .../base-files/lib/preinit/79_move_config | 19 + .../base-files/lib/upgrade/platform.sh | 165 + target/linux/phytium/config-5.10 | 743 +++ .../e2000/base-files/lib/upgrade/platform.sh | 35 + target/linux/phytium/e2000/config-5.10 | 604 ++ target/linux/phytium/e2000/target.mk | 11 + .../devicetree/bindings/dma/phytium-ddma.yaml | 55 + .../bindings/edac/phytium-pe220x-edac.txt | 23 + .../bindings/gpio/gpio-phytium-sgpio.txt | 32 + .../bindings/gpio/phytium,gpio.yaml | 113 + .../bindings/hwlock/phytium-hwspinlock.txt | 21 + .../bindings/hwmon/tacho-phytium.txt | 48 + .../devicetree/bindings/i2c/phytium,i2c.yaml | 48 + .../bindings/iio/adc/phytium-adc.txt | 57 + .../bindings/input/phytium-keypad.txt | 41 + .../interrupt-controller/phytium,ixic.txt | 28 + .../interrupt-controller/phytium,ixic.yaml | 66 + .../bindings/mailbox/phytium,mbox.yaml | 51 + .../bindings/media/phytium-jpeg.txt | 21 + .../devicetree/bindings/mmc/phytium,sdci.yaml | 58 + .../devicetree/bindings/mmc/phytium-mci.txt | 36 + .../bindings/net/can/phytium-can.txt | 29 + .../devicetree/bindings/net/phytium,gmac.yaml | 54 + .../devicetree/bindings/pwm/pwm-phytium.txt | 23 + .../bindings/remoteproc/homo-rproc.txt | 39 + .../devicetree/bindings/rng/phytium-rng.txt | 13 + .../devicetree/bindings/spi/spi-phytium.txt | 24 + .../devicetree/bindings/w1/phytium-w1.txt | 14 + .../arch/arm64/boot/dts/phytium/Makefile | 17 + .../dts/phytium/e2000d-chillipi-edu-board.dts | 339 + .../boot/dts/phytium/e2000d-demo-board.dts | 242 + .../boot/dts/phytium/e2000d-miniitx-board.dts | 296 + .../boot/dts/phytium/e2000d-power-board.dts | 243 + .../boot/dts/phytium/e2000q-come-board.dts | 265 + .../boot/dts/phytium/e2000q-demo-board.dts | 246 + .../boot/dts/phytium/e2000q-edu-board.dts | 343 ++ .../boot/dts/phytium/e2000q-hanwei-board.dts | 215 + .../boot/dts/phytium/e2000q-miniitx-board.dts | 329 + .../boot/dts/phytium/e2000q-vpx-board.dts | 322 + .../boot/dts/phytium/e2000s-demo-board.dts | 100 + .../arch/arm64/boot/dts/phytium/pe2201.dtsi | 232 + .../arch/arm64/boot/dts/phytium/pe2202.dtsi | 184 + .../arch/arm64/boot/dts/phytium/pe2204.dtsi | 216 + .../arch/arm64/boot/dts/phytium/pe220x.dtsi | 1001 +++ .../boot/dts/phytium/phytiumpi_firefly.dts | 316 + .../arch/arm64/configs/phytium_defconfig | 770 +++ .../arch/arm64/configs/phytium_optee.config | 2 + .../arm64/configs/phytiumpi_firefly_defconfig | 738 +++ .../drivers/char/hw_random/phytium-rng.c | 155 + .../drivers/char/ipmi/bt_bmc_phytium.c | 533 ++ .../drivers/char/ipmi/kcs_bmc_phytium.c | 327 + .../files-5.10/drivers/dma/phytium/Makefile | 1 + .../drivers/dma/phytium/phytium-ddmac.c | 945 +++ .../drivers/dma/phytium/phytium-ddmac.h | 164 + .../files-5.10/drivers/edac/phytium_edac.c | 485 ++ .../drivers/gpio/gpio-phytium-core.c | 363 ++ .../drivers/gpio/gpio-phytium-core.h | 87 + .../drivers/gpio/gpio-phytium-pci.c | 187 + .../drivers/gpio/gpio-phytium-platform.c | 204 + .../drivers/gpio/gpio-phytium-sgpio.c | 302 + .../drivers/gpu/drm/phytium/Kconfig | 7 + .../drivers/gpu/drm/phytium/Makefile | 18 + .../drivers/gpu/drm/phytium/pe220x_dc.c | 255 + .../drivers/gpu/drm/phytium/pe220x_dc.h | 31 + .../drivers/gpu/drm/phytium/pe220x_dp.c | 514 ++ .../drivers/gpu/drm/phytium/pe220x_dp.h | 14 + .../drivers/gpu/drm/phytium/pe220x_reg.h | 209 + .../drivers/gpu/drm/phytium/phytium_crtc.c | 829 +++ .../drivers/gpu/drm/phytium/phytium_crtc.h | 39 + .../drivers/gpu/drm/phytium/phytium_debugfs.c | 456 ++ .../drivers/gpu/drm/phytium/phytium_debugfs.h | 13 + .../gpu/drm/phytium/phytium_display_drv.c | 439 ++ .../gpu/drm/phytium/phytium_display_drv.h | 178 + .../drivers/gpu/drm/phytium/phytium_dp.c | 2662 ++++++++ .../drivers/gpu/drm/phytium/phytium_dp.h | 155 + .../drivers/gpu/drm/phytium/phytium_fb.c | 143 + .../drivers/gpu/drm/phytium/phytium_fb.h | 26 + .../drivers/gpu/drm/phytium/phytium_fbdev.c | 168 + .../drivers/gpu/drm/phytium/phytium_fbdev.h | 13 + .../drivers/gpu/drm/phytium/phytium_gem.c | 535 ++ .../drivers/gpu/drm/phytium/phytium_gem.h | 45 + .../drivers/gpu/drm/phytium/phytium_panel.c | 420 ++ .../drivers/gpu/drm/phytium/phytium_panel.h | 46 + .../drivers/gpu/drm/phytium/phytium_pci.c | 388 ++ .../drivers/gpu/drm/phytium/phytium_pci.h | 26 + .../drivers/gpu/drm/phytium/phytium_plane.c | 683 +++ .../drivers/gpu/drm/phytium/phytium_plane.h | 46 + .../gpu/drm/phytium/phytium_platform.c | 309 + .../gpu/drm/phytium/phytium_platform.h | 18 + .../drivers/gpu/drm/phytium/phytium_reg.h | 366 ++ .../drivers/gpu/drm/phytium/px210_dc.c | 326 + .../drivers/gpu/drm/phytium/px210_dc.h | 30 + .../drivers/gpu/drm/phytium/px210_dp.c | 920 +++ .../drivers/gpu/drm/phytium/px210_dp.h | 13 + .../drivers/gpu/drm/phytium/px210_reg.h | 349 ++ .../files-5.10/drivers/hwmon/tacho-phytium.c | 391 ++ .../drivers/hwspinlock/phytium_hwspinlock.c | 223 + .../drivers/i2c/busses/i2c-phytium-common.c | 203 + .../drivers/i2c/busses/i2c-phytium-core.h | 254 + .../drivers/i2c/busses/i2c-phytium-master.c | 578 ++ .../drivers/i2c/busses/i2c-phytium-pci.c | 240 + .../drivers/i2c/busses/i2c-phytium-platform.c | 364 ++ .../drivers/i2c/busses/i2c-phytium-slave.c | 262 + .../files-5.10/drivers/iio/adc/phytium-adc.c | 688 +++ .../drivers/input/keyboard/phytium-keypad.c | 586 ++ .../drivers/input/serio/phytium-ps2.c | 186 + .../drivers/irqchip/irq-phytium-ixic.c | 264 + .../drivers/mailbox/phytium-mailbox.c | 214 + .../media/platform/phytium-jpeg/Makefile | 3 + .../platform/phytium-jpeg/phytium_jpeg_core.c | 1379 +++++ .../platform/phytium-jpeg/phytium_jpeg_core.h | 147 + .../platform/phytium-jpeg/phytium_jpeg_reg.h | 113 + .../drivers/mfd/phytium_px210_i2s_lsd.c | 131 + .../drivers/mfd/phytium_px210_i2s_mmd.c | 185 + .../drivers/mmc/host/phytium-mci-pci.c | 175 + .../drivers/mmc/host/phytium-mci-plat.c | 175 + .../files-5.10/drivers/mmc/host/phytium-mci.c | 1564 +++++ .../files-5.10/drivers/mmc/host/phytium-mci.h | 357 ++ .../drivers/mmc/host/phytium-sdci.c | 1433 +++++ .../drivers/mmc/host/phytium-sdci.h | 200 + .../drivers/mtd/nand/raw/phytium_nand.c | 2167 +++++++ .../drivers/mtd/nand/raw/phytium_nand.h | 449 ++ .../drivers/mtd/nand/raw/phytium_nand_pci.c | 158 + .../drivers/mtd/nand/raw/phytium_nand_plat.c | 138 + .../files-5.10/drivers/mtd/spi-nor/boya.c | 21 + .../mtd/spi-nor/controllers/phytium-quadspi.c | 1024 +++ .../drivers/net/can/phytium/Kconfig | 34 + .../drivers/net/can/phytium/Makefile | 8 + .../drivers/net/can/phytium/phytium_can.c | 1156 ++++ .../drivers/net/can/phytium/phytium_can.h | 73 + .../drivers/net/can/phytium/phytium_can_pci.c | 152 + .../net/can/phytium/phytium_can_platform.c | 229 + .../ethernet/stmicro/stmmac/dwmac-phytium.c | 221 + .../files-5.10/drivers/net/phy/motorcomm.c | 2387 +++++++ .../files-5.10/drivers/pwm/pwm-phytium.c | 568 ++ .../drivers/remoteproc/homo_remoteproc.c | 349 ++ .../files-5.10/drivers/rtc/rtc-sd3068.c | 255 + .../files-5.10/drivers/spi/spi-phytium-dma.c | 555 ++ .../files-5.10/drivers/spi/spi-phytium-pci.c | 122 + .../files-5.10/drivers/spi/spi-phytium-plat.c | 204 + .../files-5.10/drivers/spi/spi-phytium-qspi.c | 801 +++ .../files-5.10/drivers/spi/spi-phytium.c | 510 ++ .../files-5.10/drivers/spi/spi-phytium.h | 213 + .../drivers/tty/serial/phytium-uart.c | 922 +++ .../files-5.10/drivers/usb/phytium/Kconfig | 8 + .../files-5.10/drivers/usb/phytium/Makefile | 5 + .../files-5.10/drivers/usb/phytium/core.c | 44 + .../files-5.10/drivers/usb/phytium/core.h | 98 + .../files-5.10/drivers/usb/phytium/dma.c | 786 +++ .../files-5.10/drivers/usb/phytium/dma.h | 198 + .../files-5.10/drivers/usb/phytium/gadget.c | 2538 ++++++++ .../files-5.10/drivers/usb/phytium/gadget.h | 253 + .../files-5.10/drivers/usb/phytium/host.c | 2671 ++++++++ .../files-5.10/drivers/usb/phytium/host_api.h | 248 + .../files-5.10/drivers/usb/phytium/hw-regs.h | 297 + .../files-5.10/drivers/usb/phytium/pci.c | 208 + .../files-5.10/drivers/usb/phytium/platform.c | 216 + .../drivers/w1/masters/phytium_w1.c | 643 ++ .../files-5.10/sound/pci/hda/hda_phytium.c | 1125 ++++ .../files-5.10/sound/pci/hda/hda_phytium.h | 34 + .../files-5.10/sound/soc/codecs/es8336.c | 1081 ++++ .../files-5.10/sound/soc/codecs/es8336.h | 161 + .../files-5.10/sound/soc/codecs/es8388.c | 820 +++ .../files-5.10/sound/soc/codecs/es8388.h | 290 + .../files-5.10/sound/soc/phytium/Kconfig | 30 + .../files-5.10/sound/soc/phytium/Makefile | 13 + .../files-5.10/sound/soc/phytium/local.h | 328 + .../sound/soc/phytium/phytium_i2s.c | 1424 +++++ .../files-5.10/sound/soc/phytium/pmdk_dp.c | 235 + .../sound/soc/phytium/pmdk_es8336.c | 96 + .../sound/soc/phytium/pmdk_es8388.c | 169 + target/linux/phytium/image/Makefile | 16 + target/linux/phytium/image/armv8.mk | 111 + .../phytium/image/bin/e2000_miniitx_uboot.bin | Bin 0 -> 3145728 bytes .../phytium/image/bin/e2000d_demo_uboot.bin | Bin 0 -> 3145728 bytes .../phytium/image/bin/e2000q_demo_uboot.bin | Bin 0 -> 3145728 bytes .../linux/phytium/image/bin/fip-all-2GB.bin | Bin 0 -> 3276800 bytes .../linux/phytium/image/bin/fip-all-4GB.bin | Bin 0 -> 3276800 bytes target/linux/phytium/image/bin/uboot-env.txt | 12 + target/linux/phytium/image/dts_common.mk | 29 + target/linux/phytium/image/e2000.mk | 53 + target/linux/phytium/image/grub-efi.cfg | 14 + target/linux/phytium/image/phytiumpi.mk | 41 + target/linux/phytium/modules.mk | 30 + .../001-add-phytium-support.patch | 5462 +++++++++++++++++ .../patches-5.10/002-fix-build-fail.patch | 13 + .../phytium/phytiumpi/base-files/etc/inittab | 4 + .../base-files/lib/upgrade/platform.sh | 47 + target/linux/phytium/phytiumpi/config-5.10 | 682 ++ target/linux/phytium/phytiumpi/target.mk | 14 + 203 files changed, 67909 insertions(+), 4 deletions(-) create mode 100644 target/linux/phytium/Makefile create mode 100644 target/linux/phytium/README create mode 100644 target/linux/phytium/armv8/config-5.10 create mode 100644 target/linux/phytium/armv8/target.mk create mode 100644 target/linux/phytium/base-files.mk create mode 100644 target/linux/phytium/base-files/etc/board.d/01_led create mode 100644 target/linux/phytium/base-files/etc/board.d/02_network create mode 100644 target/linux/phytium/base-files/etc/board.d/03_gpio_switches create mode 100644 target/linux/phytium/base-files/etc/inittab create mode 100644 target/linux/phytium/base-files/lib/preinit/01_sysinfo_acpi create mode 100644 target/linux/phytium/base-files/lib/preinit/79_move_config create mode 100644 target/linux/phytium/base-files/lib/upgrade/platform.sh create mode 100644 target/linux/phytium/config-5.10 create mode 100755 target/linux/phytium/e2000/base-files/lib/upgrade/platform.sh create mode 100644 target/linux/phytium/e2000/config-5.10 create mode 100644 target/linux/phytium/e2000/target.mk create mode 100644 target/linux/phytium/files-5.10/Documentation/devicetree/bindings/dma/phytium-ddma.yaml create mode 100644 target/linux/phytium/files-5.10/Documentation/devicetree/bindings/edac/phytium-pe220x-edac.txt create mode 100644 target/linux/phytium/files-5.10/Documentation/devicetree/bindings/gpio/gpio-phytium-sgpio.txt create mode 100755 target/linux/phytium/files-5.10/Documentation/devicetree/bindings/gpio/phytium,gpio.yaml create mode 100644 target/linux/phytium/files-5.10/Documentation/devicetree/bindings/hwlock/phytium-hwspinlock.txt create mode 100755 target/linux/phytium/files-5.10/Documentation/devicetree/bindings/hwmon/tacho-phytium.txt create mode 100755 target/linux/phytium/files-5.10/Documentation/devicetree/bindings/i2c/phytium,i2c.yaml create mode 100755 target/linux/phytium/files-5.10/Documentation/devicetree/bindings/iio/adc/phytium-adc.txt create mode 100755 target/linux/phytium/files-5.10/Documentation/devicetree/bindings/input/phytium-keypad.txt create mode 100644 target/linux/phytium/files-5.10/Documentation/devicetree/bindings/interrupt-controller/phytium,ixic.txt create mode 100755 target/linux/phytium/files-5.10/Documentation/devicetree/bindings/interrupt-controller/phytium,ixic.yaml create mode 100755 target/linux/phytium/files-5.10/Documentation/devicetree/bindings/mailbox/phytium,mbox.yaml create mode 100755 target/linux/phytium/files-5.10/Documentation/devicetree/bindings/media/phytium-jpeg.txt create mode 100755 target/linux/phytium/files-5.10/Documentation/devicetree/bindings/mmc/phytium,sdci.yaml create mode 100755 target/linux/phytium/files-5.10/Documentation/devicetree/bindings/mmc/phytium-mci.txt create mode 100755 target/linux/phytium/files-5.10/Documentation/devicetree/bindings/net/can/phytium-can.txt create mode 100755 target/linux/phytium/files-5.10/Documentation/devicetree/bindings/net/phytium,gmac.yaml create mode 100755 target/linux/phytium/files-5.10/Documentation/devicetree/bindings/pwm/pwm-phytium.txt create mode 100644 target/linux/phytium/files-5.10/Documentation/devicetree/bindings/remoteproc/homo-rproc.txt create mode 100755 target/linux/phytium/files-5.10/Documentation/devicetree/bindings/rng/phytium-rng.txt create mode 100755 target/linux/phytium/files-5.10/Documentation/devicetree/bindings/spi/spi-phytium.txt create mode 100755 target/linux/phytium/files-5.10/Documentation/devicetree/bindings/w1/phytium-w1.txt create mode 100644 target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/Makefile create mode 100644 target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/e2000d-chillipi-edu-board.dts create mode 100644 target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/e2000d-demo-board.dts create mode 100644 target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/e2000d-miniitx-board.dts create mode 100755 target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/e2000d-power-board.dts create mode 100755 target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/e2000q-come-board.dts create mode 100644 target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/e2000q-demo-board.dts create mode 100755 target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/e2000q-edu-board.dts create mode 100755 target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/e2000q-hanwei-board.dts create mode 100644 target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/e2000q-miniitx-board.dts create mode 100755 target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/e2000q-vpx-board.dts create mode 100644 target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/e2000s-demo-board.dts create mode 100644 target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/pe2201.dtsi create mode 100644 target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/pe2202.dtsi create mode 100644 target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/pe2204.dtsi create mode 100644 target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/pe220x.dtsi create mode 100644 target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/phytiumpi_firefly.dts create mode 100644 target/linux/phytium/files-5.10/arch/arm64/configs/phytium_defconfig create mode 100644 target/linux/phytium/files-5.10/arch/arm64/configs/phytium_optee.config create mode 100644 target/linux/phytium/files-5.10/arch/arm64/configs/phytiumpi_firefly_defconfig create mode 100644 target/linux/phytium/files-5.10/drivers/char/hw_random/phytium-rng.c create mode 100755 target/linux/phytium/files-5.10/drivers/char/ipmi/bt_bmc_phytium.c create mode 100755 target/linux/phytium/files-5.10/drivers/char/ipmi/kcs_bmc_phytium.c create mode 100644 target/linux/phytium/files-5.10/drivers/dma/phytium/Makefile create mode 100644 target/linux/phytium/files-5.10/drivers/dma/phytium/phytium-ddmac.c create mode 100644 target/linux/phytium/files-5.10/drivers/dma/phytium/phytium-ddmac.h create mode 100644 target/linux/phytium/files-5.10/drivers/edac/phytium_edac.c create mode 100644 target/linux/phytium/files-5.10/drivers/gpio/gpio-phytium-core.c create mode 100644 target/linux/phytium/files-5.10/drivers/gpio/gpio-phytium-core.h create mode 100644 target/linux/phytium/files-5.10/drivers/gpio/gpio-phytium-pci.c create mode 100644 target/linux/phytium/files-5.10/drivers/gpio/gpio-phytium-platform.c create mode 100644 target/linux/phytium/files-5.10/drivers/gpio/gpio-phytium-sgpio.c create mode 100644 target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/Kconfig create mode 100644 target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/Makefile create mode 100644 target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/pe220x_dc.c create mode 100644 target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/pe220x_dc.h create mode 100644 target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/pe220x_dp.c create mode 100644 target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/pe220x_dp.h create mode 100644 target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/pe220x_reg.h create mode 100644 target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_crtc.c create mode 100644 target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_crtc.h create mode 100644 target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_debugfs.c create mode 100644 target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_debugfs.h create mode 100644 target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_display_drv.c create mode 100644 target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_display_drv.h create mode 100644 target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_dp.c create mode 100644 target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_dp.h create mode 100644 target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_fb.c create mode 100644 target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_fb.h create mode 100644 target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_fbdev.c create mode 100644 target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_fbdev.h create mode 100644 target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_gem.c create mode 100644 target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_gem.h create mode 100644 target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_panel.c create mode 100644 target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_panel.h create mode 100644 target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_pci.c create mode 100644 target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_pci.h create mode 100644 target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_plane.c create mode 100644 target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_plane.h create mode 100644 target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_platform.c create mode 100644 target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_platform.h create mode 100644 target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_reg.h create mode 100644 target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/px210_dc.c create mode 100644 target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/px210_dc.h create mode 100644 target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/px210_dp.c create mode 100644 target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/px210_dp.h create mode 100644 target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/px210_reg.h create mode 100644 target/linux/phytium/files-5.10/drivers/hwmon/tacho-phytium.c create mode 100644 target/linux/phytium/files-5.10/drivers/hwspinlock/phytium_hwspinlock.c create mode 100644 target/linux/phytium/files-5.10/drivers/i2c/busses/i2c-phytium-common.c create mode 100644 target/linux/phytium/files-5.10/drivers/i2c/busses/i2c-phytium-core.h create mode 100644 target/linux/phytium/files-5.10/drivers/i2c/busses/i2c-phytium-master.c create mode 100644 target/linux/phytium/files-5.10/drivers/i2c/busses/i2c-phytium-pci.c create mode 100644 target/linux/phytium/files-5.10/drivers/i2c/busses/i2c-phytium-platform.c create mode 100644 target/linux/phytium/files-5.10/drivers/i2c/busses/i2c-phytium-slave.c create mode 100755 target/linux/phytium/files-5.10/drivers/iio/adc/phytium-adc.c create mode 100644 target/linux/phytium/files-5.10/drivers/input/keyboard/phytium-keypad.c create mode 100644 target/linux/phytium/files-5.10/drivers/input/serio/phytium-ps2.c create mode 100755 target/linux/phytium/files-5.10/drivers/irqchip/irq-phytium-ixic.c create mode 100644 target/linux/phytium/files-5.10/drivers/mailbox/phytium-mailbox.c create mode 100644 target/linux/phytium/files-5.10/drivers/media/platform/phytium-jpeg/Makefile create mode 100644 target/linux/phytium/files-5.10/drivers/media/platform/phytium-jpeg/phytium_jpeg_core.c create mode 100644 target/linux/phytium/files-5.10/drivers/media/platform/phytium-jpeg/phytium_jpeg_core.h create mode 100644 target/linux/phytium/files-5.10/drivers/media/platform/phytium-jpeg/phytium_jpeg_reg.h create mode 100644 target/linux/phytium/files-5.10/drivers/mfd/phytium_px210_i2s_lsd.c create mode 100644 target/linux/phytium/files-5.10/drivers/mfd/phytium_px210_i2s_mmd.c create mode 100644 target/linux/phytium/files-5.10/drivers/mmc/host/phytium-mci-pci.c create mode 100644 target/linux/phytium/files-5.10/drivers/mmc/host/phytium-mci-plat.c create mode 100644 target/linux/phytium/files-5.10/drivers/mmc/host/phytium-mci.c create mode 100644 target/linux/phytium/files-5.10/drivers/mmc/host/phytium-mci.h create mode 100755 target/linux/phytium/files-5.10/drivers/mmc/host/phytium-sdci.c create mode 100755 target/linux/phytium/files-5.10/drivers/mmc/host/phytium-sdci.h create mode 100644 target/linux/phytium/files-5.10/drivers/mtd/nand/raw/phytium_nand.c create mode 100644 target/linux/phytium/files-5.10/drivers/mtd/nand/raw/phytium_nand.h create mode 100644 target/linux/phytium/files-5.10/drivers/mtd/nand/raw/phytium_nand_pci.c create mode 100644 target/linux/phytium/files-5.10/drivers/mtd/nand/raw/phytium_nand_plat.c create mode 100644 target/linux/phytium/files-5.10/drivers/mtd/spi-nor/boya.c create mode 100644 target/linux/phytium/files-5.10/drivers/mtd/spi-nor/controllers/phytium-quadspi.c create mode 100644 target/linux/phytium/files-5.10/drivers/net/can/phytium/Kconfig create mode 100644 target/linux/phytium/files-5.10/drivers/net/can/phytium/Makefile create mode 100644 target/linux/phytium/files-5.10/drivers/net/can/phytium/phytium_can.c create mode 100644 target/linux/phytium/files-5.10/drivers/net/can/phytium/phytium_can.h create mode 100644 target/linux/phytium/files-5.10/drivers/net/can/phytium/phytium_can_pci.c create mode 100644 target/linux/phytium/files-5.10/drivers/net/can/phytium/phytium_can_platform.c create mode 100755 target/linux/phytium/files-5.10/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c create mode 100755 target/linux/phytium/files-5.10/drivers/net/phy/motorcomm.c create mode 100644 target/linux/phytium/files-5.10/drivers/pwm/pwm-phytium.c create mode 100644 target/linux/phytium/files-5.10/drivers/remoteproc/homo_remoteproc.c create mode 100755 target/linux/phytium/files-5.10/drivers/rtc/rtc-sd3068.c create mode 100644 target/linux/phytium/files-5.10/drivers/spi/spi-phytium-dma.c create mode 100644 target/linux/phytium/files-5.10/drivers/spi/spi-phytium-pci.c create mode 100644 target/linux/phytium/files-5.10/drivers/spi/spi-phytium-plat.c create mode 100755 target/linux/phytium/files-5.10/drivers/spi/spi-phytium-qspi.c create mode 100644 target/linux/phytium/files-5.10/drivers/spi/spi-phytium.c create mode 100644 target/linux/phytium/files-5.10/drivers/spi/spi-phytium.h create mode 100644 target/linux/phytium/files-5.10/drivers/tty/serial/phytium-uart.c create mode 100644 target/linux/phytium/files-5.10/drivers/usb/phytium/Kconfig create mode 100644 target/linux/phytium/files-5.10/drivers/usb/phytium/Makefile create mode 100644 target/linux/phytium/files-5.10/drivers/usb/phytium/core.c create mode 100644 target/linux/phytium/files-5.10/drivers/usb/phytium/core.h create mode 100644 target/linux/phytium/files-5.10/drivers/usb/phytium/dma.c create mode 100644 target/linux/phytium/files-5.10/drivers/usb/phytium/dma.h create mode 100644 target/linux/phytium/files-5.10/drivers/usb/phytium/gadget.c create mode 100644 target/linux/phytium/files-5.10/drivers/usb/phytium/gadget.h create mode 100644 target/linux/phytium/files-5.10/drivers/usb/phytium/host.c create mode 100644 target/linux/phytium/files-5.10/drivers/usb/phytium/host_api.h create mode 100644 target/linux/phytium/files-5.10/drivers/usb/phytium/hw-regs.h create mode 100644 target/linux/phytium/files-5.10/drivers/usb/phytium/pci.c create mode 100644 target/linux/phytium/files-5.10/drivers/usb/phytium/platform.c create mode 100644 target/linux/phytium/files-5.10/drivers/w1/masters/phytium_w1.c create mode 100644 target/linux/phytium/files-5.10/sound/pci/hda/hda_phytium.c create mode 100644 target/linux/phytium/files-5.10/sound/pci/hda/hda_phytium.h create mode 100644 target/linux/phytium/files-5.10/sound/soc/codecs/es8336.c create mode 100644 target/linux/phytium/files-5.10/sound/soc/codecs/es8336.h create mode 100644 target/linux/phytium/files-5.10/sound/soc/codecs/es8388.c create mode 100644 target/linux/phytium/files-5.10/sound/soc/codecs/es8388.h create mode 100755 target/linux/phytium/files-5.10/sound/soc/phytium/Kconfig create mode 100644 target/linux/phytium/files-5.10/sound/soc/phytium/Makefile create mode 100644 target/linux/phytium/files-5.10/sound/soc/phytium/local.h create mode 100755 target/linux/phytium/files-5.10/sound/soc/phytium/phytium_i2s.c create mode 100755 target/linux/phytium/files-5.10/sound/soc/phytium/pmdk_dp.c create mode 100644 target/linux/phytium/files-5.10/sound/soc/phytium/pmdk_es8336.c create mode 100644 target/linux/phytium/files-5.10/sound/soc/phytium/pmdk_es8388.c create mode 100644 target/linux/phytium/image/Makefile create mode 100644 target/linux/phytium/image/armv8.mk create mode 100644 target/linux/phytium/image/bin/e2000_miniitx_uboot.bin create mode 100644 target/linux/phytium/image/bin/e2000d_demo_uboot.bin create mode 100644 target/linux/phytium/image/bin/e2000q_demo_uboot.bin create mode 100644 target/linux/phytium/image/bin/fip-all-2GB.bin create mode 100644 target/linux/phytium/image/bin/fip-all-4GB.bin create mode 100644 target/linux/phytium/image/bin/uboot-env.txt create mode 100644 target/linux/phytium/image/dts_common.mk create mode 100644 target/linux/phytium/image/e2000.mk create mode 100644 target/linux/phytium/image/grub-efi.cfg create mode 100644 target/linux/phytium/image/phytiumpi.mk create mode 100644 target/linux/phytium/modules.mk create mode 100644 target/linux/phytium/patches-5.10/001-add-phytium-support.patch create mode 100644 target/linux/phytium/patches-5.10/002-fix-build-fail.patch create mode 100644 target/linux/phytium/phytiumpi/base-files/etc/inittab create mode 100755 target/linux/phytium/phytiumpi/base-files/lib/upgrade/platform.sh create mode 100644 target/linux/phytium/phytiumpi/config-5.10 create mode 100644 target/linux/phytium/phytiumpi/target.mk diff --git a/config/Config-images.in b/config/Config-images.in index 4223e45b9..40b322474 100644 --- a/config/Config-images.in +++ b/config/Config-images.in @@ -206,12 +206,13 @@ menu "Target Images" config GRUB_EFI_IMAGES bool "Build GRUB EFI images" - depends on TARGET_x86 || TARGET_armvirt || TARGET_loongarch64 + depends on TARGET_x86 || TARGET_armvirt || TARGET_loongarch64 || TARGET_phytium_armv8 depends on TARGET_ROOTFS_EXT4FS || TARGET_ROOTFS_JFFS2 || TARGET_ROOTFS_SQUASHFS select PACKAGE_grub2 if TARGET_x86 select PACKAGE_grub2-efi if TARGET_x86 select PACKAGE_grub2-bios-setup if TARGET_x86 select PACKAGE_grub2-efi-arm if TARGET_armvirt + select PACKAGE_grub2-efi-arm if TARGET_phytium_armv8 select PACKAGE_kmod-fs-vfat default y diff --git a/package/boot/grub2/Makefile b/package/boot/grub2/Makefile index be941e473..a33470b3a 100644 --- a/package/boot/grub2/Makefile +++ b/package/boot/grub2/Makefile @@ -42,6 +42,7 @@ endef Package/grub2=$(call Package/grub2/Default,x86,pc) Package/grub2-efi=$(call Package/grub2/Default,x86,efi) Package/grub2-efi-arm=$(call Package/grub2/Default,armsr,efi) +Package/grub2-efi-arm=$(call Package/grub2/Default,phytium_armv8,efi) Package/grub2-efi-loongarch64=$(call Package/grub2/Default,loongarch64,efi) define Package/grub2-editenv diff --git a/package/kernel/linux/modules/video.mk b/package/kernel/linux/modules/video.mk index e18ee4478..6b6fbbc9c 100644 --- a/package/kernel/linux/modules/video.mk +++ b/package/kernel/linux/modules/video.mk @@ -57,7 +57,7 @@ $(eval $(call KernelPackage,backlight-pwm)) define KernelPackage/acpi-video SUBMENU:=$(VIDEO_MENU) TITLE:=ACPI Extensions For Display Adapters - DEPENDS:=@TARGET_x86||TARGET_loongarch64 +kmod-backlight + DEPENDS:=@TARGET_x86||TARGET_loongarch64||TARGET_phytium +kmod-backlight HIDDEN:=1 KCONFIG:=CONFIG_ACPI_VIDEO FILES:=$(LINUX_DIR)/drivers/acpi/video.ko @@ -386,7 +386,7 @@ $(eval $(call KernelPackage,drm-display-helper)) define KernelPackage/drm-amdgpu SUBMENU:=$(VIDEO_MENU) TITLE:=AMDGPU DRM support - DEPENDS:=@TARGET_x86||TARGET_loongarch64 @DISPLAY_SUPPORT +kmod-backlight +kmod-drm-ttm \ + DEPENDS:=@TARGET_x86||TARGET_loongarch64||TARGET_phytium @DISPLAY_SUPPORT +kmod-backlight +kmod-drm-ttm \ +kmod-drm-ttm-helper +kmod-drm-kms-helper +kmod-i2c-algo-bit +amdgpu-firmware \ +kmod-drm-display-helper +kmod-drm-buddy +kmod-acpi-video KCONFIG:=CONFIG_DRM_AMDGPU \ @@ -525,7 +525,7 @@ $(eval $(call KernelPackage,drm-panfrost)) define KernelPackage/drm-radeon SUBMENU:=$(VIDEO_MENU) TITLE:=Radeon DRM support - DEPENDS:=@TARGET_x86 @DISPLAY_SUPPORT +kmod-backlight +kmod-drm-kms-helper \ + DEPENDS:=@TARGET_x86||TARGET_phytium @DISPLAY_SUPPORT +kmod-backlight +kmod-drm-kms-helper \ +kmod-drm-ttm +kmod-i2c-algo-bit +LINUX_6_1:kmod-acpi-video +radeon-firmware KCONFIG:=CONFIG_DRM_RADEON FILES:=$(LINUX_DIR)/drivers/gpu/drm/radeon/radeon.ko diff --git a/target/linux/phytium/Makefile b/target/linux/phytium/Makefile new file mode 100644 index 000000000..8c2d3d9ca --- /dev/null +++ b/target/linux/phytium/Makefile @@ -0,0 +1,24 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Copyright (c) 2023 Phytium Technology Co., Ltd. +# Copyright (C) 2023-2024 Ailick <277498654@qq.com> + +include $(TOPDIR)/rules.mk + +BOARD:=phytium +BOARDNAME:=Phytium +FEATURES:=fpu pci pcie rtc usb boot-part rootfs-part +FEATURES+=cpiogz ext4 ramdisk squashfs targz vmdk usb gpio fpu pci pcie jffs2 +SUBTARGETS:=armv8 e2000 phytiumpi + +KERNEL_PATCHVER:=5.10 + +include $(INCLUDE_DIR)/target.mk + +DEFAULT_PACKAGES += mkf2fs e2fsprogs +# blkid used for resolving PARTUUID +# in sysupgrade. vfat required for +# mounting ESP partition +DEFAULT_PACKAGES += blkid kmod-fs-vfat kmod-usb2 kmod-usb3 kmod-hwmon-phytium + +$(eval $(call BuildTarget)) diff --git a/target/linux/phytium/README b/target/linux/phytium/README new file mode 100644 index 000000000..052badd2b --- /dev/null +++ b/target/linux/phytium/README @@ -0,0 +1,5 @@ +This target generates images that can be used on ARM machines with EFI +support (e.g EDKII/TianoCore or U-Boot with bootefi). + +Tested: +THTF ChaoXiang TF830-V050 diff --git a/target/linux/phytium/armv8/config-5.10 b/target/linux/phytium/armv8/config-5.10 new file mode 100644 index 000000000..dbaf5789c --- /dev/null +++ b/target/linux/phytium/armv8/config-5.10 @@ -0,0 +1,827 @@ +CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y +CONFIG_USELIB=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y +CONFIG_RCU_EXPERT=y +CONFIG_RCU_NOCB_CPU=y +CONFIG_RCU_FANOUT=64 +CONFIG_RCU_FANOUT_LEAF=16 +# CONFIG_RCU_FAST_NO_HZ is not set +# CONFIG_TASKS_TRACE_RCU_READ_MB is not set +# CONFIG_UCLAMP_TASK is not set +CONFIG_NUMA_BALANCING_DEFAULT_ENABLED=y +CONFIG_ZONE_DMA32=y +CONFIG_NUMA=y +CONFIG_NODES_SHIFT=8 +CONFIG_UNMAP_KERNEL_AT_EL0=y +CONFIG_RODATA_FULL_DEFAULT_ENABLED=y +CONFIG_DMI=y +# CONFIG_HIBERNATION is not set +# CONFIG_HIBERNATION_SNAPSHOT_DEV is not set +CONFIG_PM_STD_PARTITION="" +CONFIG_ARM_PSCI_CPUIDLE_DOMAIN=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_SCHEDUTIL=y +# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set +# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set +# CONFIG_ACPI_CPPC_CPUFREQ is not set +# CONFIG_ARM_SCMI_CPUFREQ is not set +# CONFIG_ARM_SCMI_TRANSPORT_FORCE_POLLING is not set +CONFIG_ARM_SCMI_POWER_DOMAIN=y +CONFIG_DMIID=y +# CONFIG_DMI_SYSFS is not set +# CONFIG_ISCSI_IBFT is not set +CONFIG_EFI_ARMSTUB_DTB_LOADER=y +CONFIG_EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER=y +# CONFIG_EFI_BOOTLOADER_CONTROL is not set +# CONFIG_EFI_TEST is not set +# CONFIG_EFI_DISABLE_PCI_DMA is not set +# CONFIG_EFI_CUSTOM_SSDT_OVERLAYS is not set +CONFIG_ACPI_DEBUGGER=y +# CONFIG_ACPI_EC_DEBUGFS is not set +CONFIG_ACPI_AC=y +CONFIG_ACPI_BATTERY=y +# CONFIG_ACPI_TINY_POWER_BUTTON is not set +CONFIG_ACPI_FAN=y +# CONFIG_ACPI_TAD is not set +CONFIG_ACPI_PROCESSOR=y +# CONFIG_ACPI_IPMI is not set +CONFIG_ACPI_THERMAL=y +CONFIG_ACPI_CUSTOM_DSDT_FILE="" +# CONFIG_ACPI_DEBUG is not set +# CONFIG_ACPI_HMAT is not set +# CONFIG_ACPI_APEI_ERST_DEBUG is not set +# CONFIG_ACPI_DEBUGGER_USER is not set +CONFIG_KVM_ARM_PMU=y +CONFIG_ARCH_MMAP_RND_BITS=18 +CONFIG_ARCH_MMAP_RND_COMPAT_BITS=11 +CONFIG_COMPAT_32BIT_TIME=y +CONFIG_BLK_WBT_MQ=y +# CONFIG_LDM_DEBUG is not set +# CONFIG_BFQ_CGROUP_DEBUG is not set +CONFIG_SPARSEMEM_VMEMMAP=y +CONFIG_BALLOON_COMPACTION=y +# CONFIG_HWPOISON_INJECT is not set +# CONFIG_ZSWAP_COMPRESSOR_DEFAULT_DEFLATE is not set +# CONFIG_ZSWAP_COMPRESSOR_DEFAULT_LZO is not set +# CONFIG_ZSWAP_COMPRESSOR_DEFAULT_842 is not set +# CONFIG_ZSWAP_COMPRESSOR_DEFAULT_LZ4 is not set +# CONFIG_ZSWAP_COMPRESSOR_DEFAULT_LZ4HC is not set +CONFIG_ZSWAP_COMPRESSOR_DEFAULT_ZSTD=y +CONFIG_ZSWAP_ZPOOL_DEFAULT_ZBUD=y +# CONFIG_ZSWAP_ZPOOL_DEFAULT_Z3FOLD is not set +# CONFIG_ZSWAP_ZPOOL_DEFAULT_ZSMALLOC is not set +# CONFIG_ZSWAP_DEFAULT_ON is not set +# CONFIG_Z3FOLD is not set +# CONFIG_BPFILTER_UMH is not set +# CONFIG_NCSI_OEM_CMD_GET_MAC is not set +# CONFIG_NET_9P_XEN is not set +# CONFIG_NET_9P_DEBUG is not set +# CONFIG_NFC_DIGITAL is not set +# CONFIG_NFC_NCI_SPI is not set +# CONFIG_NFC_NCI_UART is not set +# CONFIG_NFC_HCI is not set +# CONFIG_NFC_FDP is not set +# CONFIG_NFC_PN533_USB is not set +# CONFIG_NFC_PN533_I2C is not set +# CONFIG_NFC_PN532_UART is not set +# CONFIG_NFC_MRVL_USB is not set +# CONFIG_NFC_ST_NCI_I2C is not set +# CONFIG_NFC_ST_NCI_SPI is not set +# CONFIG_NFC_NXP_NCI is not set +# CONFIG_HOTPLUG_PCI_ACPI_IBM is not set +CONFIG_PCI_XGENE_MSI=y +# CONFIG_PCIE_HISI_ERR is not set +# CONFIG_PCIE_CADENCE_PLAT_EP is not set +# CONFIG_PCI_J721E_EP is not set +CONFIG_FW_CACHE=y +CONFIG_PROC_EVENTS=y +CONFIG_MTD_PHYSMAP_START=0x8000000 +CONFIG_MTD_PHYSMAP_BANKWIDTH=2 +# CONFIG_MTD_DATAFLASH_WRITE_VERIFY is not set +# CONFIG_MTD_DATAFLASH_OTP is not set +# CONFIG_MTD_NAND_PHYTIUM_PCI is not set +# CONFIG_SPI_HISI_SFC is not set +CONFIG_XEN_BLKDEV_FRONTEND=y +# CONFIG_XEN_BLKDEV_BACKEND is not set +CONFIG_SCSI_SAS_HOST_SMP=y +# CONFIG_MEGARAID_MM is not set +CONFIG_SCSI_MPT2SAS_MAX_SGE=128 +CONFIG_SCSI_MPT3SAS_MAX_SGE=128 +# CONFIG_SCSI_UFSHCD_PCI is not set +# CONFIG_SCSI_UFS_CDNS_PLATFORM is not set +# CONFIG_SCSI_UFS_DWC_TC_PLATFORM is not set +# CONFIG_SCSI_UFS_BSG is not set +# CONFIG_XEN_SCSI_FRONTEND is not set +# CONFIG_SCSI_DH_RDAC is not set +# CONFIG_SCSI_DH_HP_SW is not set +# CONFIG_SCSI_DH_EMC is not set +# CONFIG_SCSI_DH_ALUA is not set +# CONFIG_DM_INIT is not set +# CONFIG_DM_ZONED is not set +CONFIG_FUSION_MAX_SGE=128 +# CONFIG_MACB_PCI is not set +# CONFIG_HNS3_HCLGEVF is not set +# CONFIG_STMMAC_SELFTESTS is not set +# CONFIG_ROADRUNNER is not set +# CONFIG_MOTORCOMM_PHY is not set +# CONFIG_DLCI is not set +CONFIG_XEN_NETDEV_FRONTEND=y +# CONFIG_XEN_NETDEV_BACKEND is not set +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +# CONFIG_INPUT_JOYSTICK is not set +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_MEMCG=y +CONFIG_BLK_CGROUP=y +CONFIG_CFS_BANDWIDTH=y +CONFIG_RT_GROUP_SCHED=y +CONFIG_CGROUP_PIDS=y +CONFIG_CGROUP_RDMA=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_HUGETLB=y +CONFIG_CPUSETS=y +CONFIG_CGROUP_DEVICE=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_CGROUP_BPF=y +CONFIG_SCHED_AUTOGROUP=y +CONFIG_RELAY=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_EXPERT=y +CONFIG_SGETMASK_SYSCALL=y +CONFIG_KALLSYMS_ALL=y +CONFIG_BPF_JIT_ALWAYS_ON=y +CONFIG_USERFAULTFD=y +# CONFIG_SLUB_DEBUG is not set +CONFIG_SLUB_MEMCG_SYSFS_ON=y +# CONFIG_COMPAT_BRK is not set +# CONFIG_SLAB_MERGE_DEFAULT is not set +CONFIG_PROFILING=y +CONFIG_ARCH_HISI=y +CONFIG_ARCH_VEXPRESS=y +CONFIG_ARCH_PHYTIUM=y +CONFIG_ARM64_VA_BITS_48=y +CONFIG_SCHED_MC=y +CONFIG_SCHED_SMT=y +CONFIG_KEXEC=y +CONFIG_CRASH_DUMP=y +CONFIG_XEN=y +CONFIG_COMPAT=y +CONFIG_ARMV8_DEPRECATED=y +CONFIG_SWP_EMULATION=y +CONFIG_CP15_BARRIER_EMULATION=y +CONFIG_SETEND_EMULATION=y +CONFIG_ARM64_PMEM=y +CONFIG_RANDOMIZE_BASE=y +CONFIG_ARM64_ACPI_PARKING_PROTOCOL=y +CONFIG_CMDLINE="pcie_aspm=off" +CONFIG_HIBERNATION=y +CONFIG_CPU_IDLE_GOV_LADDER=y +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_STAT=y +CONFIG_ARM_SCMI_PROTOCOL=y +CONFIG_ARM_SDE_INTERFACE=y +CONFIG_FIRMWARE_MEMMAP=y +CONFIG_RESET_ATTACK_MITIGATION=y +CONFIG_ACPI=y +CONFIG_ACPI_DOCK=y +CONFIG_ACPI_PCI_SLOT=y +CONFIG_ACPI_BGRT=y +CONFIG_ACPI_APEI=y +CONFIG_ACPI_APEI_GHES=y +CONFIG_PMIC_OPREGION=y +CONFIG_VIRTUALIZATION=y +CONFIG_KVM=y +CONFIG_ARM64_CRYPTO=y +CONFIG_KPROBES=y +CONFIG_JUMP_LABEL=y +# CONFIG_VMAP_STACK is not set +CONFIG_MODVERSIONS=y +CONFIG_UNUSED_SYMBOLS=y +CONFIG_BLK_DEV_ZONED=y +CONFIG_BLK_DEV_THROTTLING=y +CONFIG_BLK_DEV_THROTTLING_LOW=y +CONFIG_BLK_CMDLINE_PARSER=y +CONFIG_BLK_WBT=y +CONFIG_BLK_CGROUP_IOLATENCY=y +# CONFIG_BLK_DEBUG_FS is not set +CONFIG_BLK_SED_OPAL=y +CONFIG_PARTITION_ADVANCED=y +CONFIG_OSF_PARTITION=y +CONFIG_ATARI_PARTITION=y +CONFIG_MAC_PARTITION=y +CONFIG_BSD_DISKLABEL=y +CONFIG_MINIX_SUBPARTITION=y +CONFIG_SOLARIS_X86_PARTITION=y +CONFIG_UNIXWARE_DISKLABEL=y +CONFIG_LDM_PARTITION=y +CONFIG_SGI_PARTITION=y +CONFIG_ULTRIX_PARTITION=y +CONFIG_SUN_PARTITION=y +CONFIG_KARMA_PARTITION=y +CONFIG_SYSV68_PARTITION=y +CONFIG_IOSCHED_BFQ=y +CONFIG_BFQ_GROUP_IOSCHED=y +CONFIG_DEFAULT_MMAP_MIN_ADDR=65536 +CONFIG_MEMORY_FAILURE=y +CONFIG_TRANSPARENT_HUGEPAGE=y +CONFIG_FRONTSWAP=y +CONFIG_ZSWAP=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_PACKET_DIAG=y +CONFIG_UNIX=y +CONFIG_UNIX_DIAG=y +CONFIG_XFRM_SUB_POLICY=y +CONFIG_XDP_SOCKETS=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_FIB_TRIE_STATS=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_ROUTE_MULTIPATH=y +CONFIG_IP_ROUTE_VERBOSE=y +CONFIG_IP_PNP=y +CONFIG_IP_MROUTE=y +CONFIG_IP_MROUTE_MULTIPLE_TABLES=y +CONFIG_IP_PIMSM_V1=y +CONFIG_IP_PIMSM_V2=y +CONFIG_NET_FOU=y +CONFIG_NET_FOU_IP_TUNNELS=y +CONFIG_INET_DIAG_DESTROY=y +CONFIG_TCP_CONG_ADVANCED=y +CONFIG_TCP_MD5SIG=y +# CONFIG_IPV6_SIT is not set +CONFIG_IPV6_SUBTREES=y +CONFIG_IPV6_MROUTE=y +CONFIG_IPV6_MROUTE_MULTIPLE_TABLES=y +CONFIG_IPV6_PIMSM_V2=y +CONFIG_IPV6_SEG6_LWTUNNEL=y +CONFIG_IPV6_SEG6_HMAC=y +# CONFIG_NETWORK_PHY_TIMESTAMPING is not set +CONFIG_NETFILTER=y +CONFIG_NF_CONNTRACK_ZONES=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CONNTRACK_TIMEOUT=y +CONFIG_NF_CONNTRACK_TIMESTAMP=y +CONFIG_IP_VS_IPV6=y +CONFIG_IP_VS_PROTO_TCP=y +CONFIG_IP_VS_PROTO_UDP=y +CONFIG_IP_VS_PROTO_ESP=y +CONFIG_IP_VS_PROTO_AH=y +CONFIG_IP_VS_PROTO_SCTP=y +CONFIG_NF_REJECT_IPV4=y +CONFIG_BPFILTER=y +CONFIG_BRIDGE_VLAN_FILTERING=y +CONFIG_VLAN_8021Q=y +CONFIG_VLAN_8021Q_GVRP=y +CONFIG_VLAN_8021Q_MVRP=y +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_DEFAULT=y +CONFIG_NET_EMATCH=y +CONFIG_NET_CLS_ACT=y +CONFIG_DCB=y +CONFIG_NET_NCSI=y +CONFIG_CGROUP_NET_PRIO=y +CONFIG_BPF_JIT=y +CONFIG_BPF_STREAM_PARSER=y +CONFIG_PCI=y +CONFIG_PCIEPORTBUS=y +CONFIG_HOTPLUG_PCI_PCIE=y +CONFIG_PCIE_PTM=y +CONFIG_PCI_STUB=y +CONFIG_PCI_IOV=y +CONFIG_PCI_PRI=y +CONFIG_PCI_PASID=y +CONFIG_HOTPLUG_PCI=y +CONFIG_HOTPLUG_PCI_ACPI=y +CONFIG_HOTPLUG_PCI_CPCI=y +CONFIG_HOTPLUG_PCI_SHPC=y +CONFIG_PCI_FTPCI100=y +CONFIG_PCI_HOST_GENERIC=y +CONFIG_PCI_XGENE=y +CONFIG_PCI_HOST_THUNDER_PEM=y +CONFIG_PCI_HOST_THUNDER_ECAM=y +CONFIG_PCIE_DW_PLAT_HOST=y +CONFIG_PCIE_DW_PLAT_EP=y +CONFIG_PCI_HISI=y +CONFIG_PCIE_KIRIN=y +CONFIG_PCIE_HISI_STB=y +CONFIG_PCI_ENDPOINT=y +CONFIG_PCI_ENDPOINT_CONFIGFS=y +CONFIG_DEVTMPFS=y +# CONFIG_STANDALONE is not set +# CONFIG_PREVENT_FIRMWARE_BUILD is not set +CONFIG_FW_LOADER_USER_HELPER=y +CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y +# CONFIG_ALLOW_DEV_COREDUMP is not set +CONFIG_BRCMSTB_GISB_ARB=y +CONFIG_CONNECTOR=y +CONFIG_MTD_PARTITIONED_MASTER=y +CONFIG_MTD_CFI_ADV_OPTIONS=y +CONFIG_MTD_CFI_GEOMETRY=y +CONFIG_MTD_MAP_BANK_WIDTH_8=y +CONFIG_MTD_MAP_BANK_WIDTH_16=y +CONFIG_MTD_MAP_BANK_WIDTH_32=y +CONFIG_MTD_CFI_I4=y +CONFIG_MTD_CFI_I8=y +CONFIG_MTD_OTP=y +CONFIG_MTD_PHYSMAP_COMPAT=y +CONFIG_MTD_PHYSMAP_LEN=0x0 +CONFIG_SPI_PHYTIUM_QUADSPI=y +# CONFIG_PNP_DEBUG_MESSAGES is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=8 +CONFIG_BLK_DEV_RAM_SIZE=32768 +CONFIG_BLK_DEV_NVME=y +CONFIG_NVME_MULTIPATH=y +CONFIG_SRAM=y +CONFIG_BLK_DEV_SD=y +CONFIG_BLK_DEV_SR=y +CONFIG_CHR_DEV_SG=y +CONFIG_CHR_DEV_SCH=y +CONFIG_SCSI_CONSTANTS=y +CONFIG_SCSI_LOGGING=y +CONFIG_SCSI_SCAN_ASYNC=y +CONFIG_SCSI_SAS_ATA=y +CONFIG_MEGARAID_NEWGEN=y +CONFIG_SCSI_DH=y +CONFIG_ATA=y +# CONFIG_ATA_ACPI is not set +CONFIG_SATA_AHCI=y +CONFIG_MD=y +CONFIG_BLK_DEV_DM=y +CONFIG_DM_UEVENT=y +CONFIG_FUSION=y +CONFIG_FUSION_LOGGING=y +CONFIG_NET_FC=y +# CONFIG_NET_VENDOR_AMAZON is not set +CONFIG_AMD_XGBE_DCB=y +CONFIG_NET_TULIP=y +CONFIG_HNS3_DCB=y +CONFIG_IXGBE_DCB=y +CONFIG_I40E_DCB=y +# CONFIG_MLX4_DEBUG is not set +# CONFIG_NET_VENDOR_NETRONOME is not set +CONFIG_STMMAC_ETH=y +CONFIG_STMMAC_PCI=y +CONFIG_HIPPI=y +CONFIG_MARVELL_PHY=y +CONFIG_MDIO_HISI_FEMAC=y +CONFIG_PPP_FILTER=y +CONFIG_PPP_MULTILINK=y +CONFIG_WIRELESS_WDS=y +CONFIG_WAN=y +CONFIG_ISDN=y +# CONFIG_INPUT_LEDS is not set +CONFIG_INPUT_MOUSEDEV=y +CONFIG_INPUT_MOUSEDEV_PSAUX=y +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_JOYSTICK=y +CONFIG_INPUT_TABLET=y +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_HISI_POWERKEY=y +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_FINTEK=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_NR_UARTS=48 +CONFIG_SERIAL_8250_RUNTIME_UARTS=32 +CONFIG_SERIAL_8250_EXTENDED=y +CONFIG_SERIAL_8250_MANY_PORTS=y +CONFIG_SERIAL_8250_SHARE_IRQ=y +CONFIG_SERIAL_8250_DETECT_IRQ=y +CONFIG_SERIAL_8250_DW=y +CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SERIAL_AMBA_PL010=y +CONFIG_SERIAL_AMBA_PL010_CONSOLE=y +CONFIG_SERIAL_AMBA_PL011=y +CONFIG_SERIAL_AMBA_PL011_CONSOLE=y +CONFIG_SERIAL_XILINX_PS_UART=y +CONFIG_SERIAL_XILINX_PS_UART_CONSOLE=y +CONFIG_SERIAL_NONSTANDARD=y +CONFIG_VIRTIO_CONSOLE=y +CONFIG_HW_RANDOM=y +CONFIG_I2C_SLAVE=y +CONFIG_SPI=y +CONFIG_SPI_PHYTIUM_PLAT=y +CONFIG_SPI_PHYTIUM_PCI=y +CONFIG_GPIO_SYSFS=y +CONFIG_GPIO_DWAPB=y +CONFIG_GPIO_PL061=y +CONFIG_GPIO_XGENE=y +CONFIG_POWER_RESET_GPIO=y +CONFIG_POWER_RESET_GPIO_RESTART=y +CONFIG_POWER_RESET_HISI=y +CONFIG_POWER_RESET_LTC2952=y +CONFIG_POWER_RESET_RESTART=y +CONFIG_POWER_RESET_XGENE=y +CONFIG_POWER_RESET_SYSCON=y +CONFIG_POWER_RESET_SYSCON_POWEROFF=y +CONFIG_THERMAL_GOV_FAIR_SHARE=y +CONFIG_THERMAL_GOV_BANG_BANG=y +CONFIG_THERMAL_GOV_USER_SPACE=y +CONFIG_DEVFREQ_THERMAL=y +CONFIG_WATCHDOG=y +CONFIG_PMIC_DA903X=y +CONFIG_MFD_DA9052_SPI=y +CONFIG_MFD_DA9052_I2C=y +CONFIG_MFD_DA9055=y +CONFIG_MFD_DA9063=y +CONFIG_HTC_I2CPLD=y +CONFIG_MFD_88PM860X=y +CONFIG_MFD_MAX14577=y +CONFIG_MFD_MAX77686=y +CONFIG_MFD_MAX77693=y +CONFIG_MFD_MAX77843=y +CONFIG_MFD_MAX8925=y +CONFIG_MFD_MAX8997=y +CONFIG_MFD_MAX8998=y +CONFIG_EZX_PCAP=y +CONFIG_MFD_PHYTIUM_I2S_LSD=y +CONFIG_MFD_PHYTIUM_I2S_MMD=y +CONFIG_MFD_RC5T583=y +CONFIG_MFD_SEC_CORE=y +CONFIG_ABX500_CORE=y +CONFIG_AB3100_CORE=y +CONFIG_MFD_STMPE=y +CONFIG_MFD_LP8788=y +CONFIG_MFD_PALMAS=y +CONFIG_MFD_TPS65090=y +CONFIG_MFD_TPS6586X=y +CONFIG_MFD_TPS65910=y +CONFIG_MFD_TPS65912_I2C=y +CONFIG_MFD_TPS65912_SPI=y +CONFIG_MFD_TPS80031=y +CONFIG_TWL4030_CORE=y +CONFIG_TWL6040_CORE=y +CONFIG_MFD_TC3589X=y +CONFIG_MFD_WM8400=y +CONFIG_MFD_WM831X_I2C=y +CONFIG_MFD_WM831X_SPI=y +CONFIG_MFD_WM8350_I2C=y +CONFIG_DVB_MAX_ADAPTERS=8 +CONFIG_MEDIA_USB_SUPPORT=y +CONFIG_MEDIA_PCI_SUPPORT=y +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_VIDEO_CADENCE=y +CONFIG_DVB_PLATFORM_DRIVERS=y +CONFIG_DRM=y +CONFIG_DRM_DP_AUX_CHARDEV=y +CONFIG_DRM_LOAD_EDID_FIRMWARE=y +CONFIG_DRM_DP_CEC=y +CONFIG_DRM_RADEON_USERPTR=y +CONFIG_DRM_AMDGPU_SI=y +CONFIG_DRM_AMDGPU_CIK=y +CONFIG_DRM_AMDGPU_USERPTR=y +CONFIG_DRM_AMD_ACP=y +CONFIG_DRM_VIRTIO_GPU=y +CONFIG_DRM_XEN=y +CONFIG_FIRMWARE_EDID=y +CONFIG_FB_TILEBLITTING=y +CONFIG_FB_EFI=y +CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_SOUND=y +# CONFIG_SOUND_OSS_CORE_PRECLAIM is not set +CONFIG_SND=y +CONFIG_SND_OSSEMUL=y +CONFIG_SND_MIXER_OSS=y +CONFIG_SND_PCM_OSS=y +CONFIG_SND_HRTIMER=y +CONFIG_SND_SEQUENCER=y +CONFIG_SND_SEQ_DUMMY=y +CONFIG_SND_SEQUENCER_OSS=y +CONFIG_SND_HDA_INTEL=y +CONFIG_SND_HDA_PHYTIUM=y +CONFIG_SND_HDA_HWDEP=y +CONFIG_SND_HDA_INPUT_BEEP=y +CONFIG_SND_HDA_PATCH_LOADER=y +CONFIG_SND_HDA_CODEC_REALTEK=y +CONFIG_SND_HDA_CODEC_ANALOG=y +CONFIG_SND_HDA_CODEC_SIGMATEL=y +CONFIG_SND_HDA_CODEC_VIA=y +CONFIG_SND_HDA_CODEC_HDMI=y +CONFIG_SND_HDA_CODEC_CIRRUS=y +CONFIG_SND_HDA_CODEC_CONEXANT=y +CONFIG_SND_HDA_CODEC_CA0110=y +CONFIG_SND_HDA_CODEC_CA0132=y +CONFIG_SND_HDA_CODEC_CMEDIA=y +CONFIG_SND_HDA_CODEC_SI3054=y +CONFIG_SND_HDA_PREALLOC_SIZE=1024 +CONFIG_SND_SOC_IMG=y +CONFIG_SND_SOC_PHYTIUM_I2S=y +CONFIG_HID_BATTERY_STRENGTH=y +CONFIG_HIDRAW=y +CONFIG_HID_PID=y +CONFIG_USB_LED_TRIG=y +CONFIG_USB=y +CONFIG_USB_XHCI_HCD=y +CONFIG_USB_XHCI_DBGCAP=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_OHCI_HCD=y +CONFIG_MUSB_PIO_ONLY=y +CONFIG_USB_DWC3_ULPI=y +CONFIG_USB_CHIPIDEA_HOST=y +CONFIG_USB_SERIAL=y +CONFIG_USB_SERIAL_GENERIC=y +CONFIG_USB_ULPI=y +CONFIG_MMC_PHYTIUM_SDCI=y +CONFIG_LEDS_SYSCON=y +CONFIG_LEDS_TRIGGER_CPU=y +CONFIG_EDAC=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_DS1307_CENTURY=y +CONFIG_RTC_DRV_RV8803=y +CONFIG_RTC_DRV_EFI=y +CONFIG_RTC_DRV_ZYNQMP=y +CONFIG_RTC_DRV_PL030=y +CONFIG_RTC_DRV_PL031=y +CONFIG_DMADEVICES=y +CONFIG_ASYNC_TX_DMA=y +CONFIG_SW_SYNC=y +CONFIG_AUXDISPLAY=y +CONFIG_VIRT_DRIVERS=y +CONFIG_VIRTIO_PCI=y +CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES=y +CONFIG_XEN_GNTDEV=y +CONFIG_XEN_GNTDEV_DMABUF=y +CONFIG_XEN_GRANT_DMA_ALLOC=y +CONFIG_XEN_PVCALLS_BACKEND=y +CONFIG_STAGING=y +CONFIG_STAGING_MEDIA=y +CONFIG_CHROME_PLATFORMS=y +CONFIG_CLK_QORIQ=y +CONFIG_SOC_BRCMSTB=y +CONFIG_SOC_TI=y +CONFIG_PM_DEVFREQ_EVENT=y +CONFIG_MEMORY=y +CONFIG_NTB=y +CONFIG_PWM=y +CONFIG_PHYTIUM_IXIC=y +CONFIG_POWERCAP=y +CONFIG_HISI_PMU=y +CONFIG_STM=y +CONFIG_STM_SOURCE_CONSOLE=y +CONFIG_EXT2_FS_XATTR=y +CONFIG_EXT2_FS_POSIX_ACL=y +CONFIG_EXT2_FS_SECURITY=y +CONFIG_EXT3_FS_POSIX_ACL=y +CONFIG_EXT3_FS_SECURITY=y +CONFIG_EXT4_FS=y +CONFIG_BTRFS_FS_POSIX_ACL=y +CONFIG_F2FS_FS_SECURITY=y +CONFIG_F2FS_CHECK_FS=y +CONFIG_FS_DAX=y +CONFIG_FANOTIFY=y +CONFIG_QUOTA_NETLINK_INTERFACE=y +CONFIG_AUTOFS4_FS=y +CONFIG_FSCACHE=y +CONFIG_ISO9660_FS=y +CONFIG_JOLIET=y +CONFIG_ZISOFS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_UTF8=y +CONFIG_PROC_KCORE=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_HUGETLBFS=y +CONFIG_JFFS2_FS_WBUF_VERIFY=y +CONFIG_JFFS2_SUMMARY=y +CONFIG_JFFS2_FS_XATTR=y +CONFIG_SQUASHFS=y +CONFIG_SQUASHFS_DECOMP_MULTI=y +CONFIG_SQUASHFS_XATTR=y +CONFIG_SQUASHFS_LZ4=y +CONFIG_SQUASHFS_LZO=y +CONFIG_SQUASHFS_XZ=y +CONFIG_SQUASHFS_ZSTD=y +CONFIG_PSTORE_842_COMPRESS=y +CONFIG_PSTORE_ZSTD_COMPRESS=y +CONFIG_PSTORE_CONSOLE=y +CONFIG_PSTORE_PMSG=y +CONFIG_PSTORE_RAM=y +CONFIG_NFS_V3_ACL=y +CONFIG_NFS_SWAP=y +CONFIG_NFS_V4_1=y +CONFIG_NFS_V4_2=y +CONFIG_NFS_V4_1_MIGRATION=y +CONFIG_NFS_USE_LEGACY_DNS=y +CONFIG_9P_FS_POSIX_ACL=y +CONFIG_9P_FS_SECURITY=y +CONFIG_NLS_DEFAULT="utf8" +CONFIG_PERSISTENT_KEYRINGS=y +CONFIG_ENCRYPTED_KEYS=y +CONFIG_SECURITY_DMESG_RESTRICT=y +CONFIG_SECURITY=y +# CONFIG_CRYPTO_MANAGER_DISABLE_TESTS is not set +CONFIG_CRYPTO_DRBG_HASH=y +CONFIG_CRYPTO_DRBG_CTR=y +CONFIG_INDIRECT_PIO=y +CONFIG_CRC_CCITT=y +CONFIG_LIBCRC32C=y +CONFIG_FONTS=y +CONFIG_FONT_8x8=y +CONFIG_PRINTK_TIME=y +CONFIG_DYNAMIC_DEBUG=y +CONFIG_DEBUG_INFO=y +# CONFIG_ENABLE_MUST_CHECK is not set +CONFIG_STRIP_ASM_SYMS=y +CONFIG_DEBUG_SECTION_MISMATCH=y +CONFIG_MAGIC_SYSRQ=y +# CONFIG_SCHED_DEBUG is not set +CONFIG_RCU_CPU_STALL_TIMEOUT=60 +# CONFIG_RCU_TRACE is not set +# CONFIG_STRICT_DEVMEM is not set +# CONFIG_RUNTIME_TESTING_MENU is not set +CONFIG_EFI=y +CONFIG_MTD_NAND_PHYTIUM_PLAT=y +CONFIG_SERIAL_PHYTIUM_PCI=y +CONFIG_HW_RANDOM_PHYTIUM=y +CONFIG_I2C_PHYTIUM_PCI=y +CONFIG_I2C_PHYTIUM_PLATFORM=y +CONFIG_SPI_PHYTIUM_QSPI=y +CONFIG_W1_MASTER_PHYTIUM=y +CONFIG_USB_SUPPORT=y +CONFIG_USB_PHYTIUM=y +# CONFIG_MMC_PHYTIUM_MCI_PCI is not set +CONFIG_EDAC_PHYTIUM=y +CONFIG_PHYTIUM_DDMA=y +CONFIG_HWSPINLOCK_PHYTIUM=y +CONFIG_PHYTIUM_ADC=y +CONFIG_PWM_PHYTIUM=y +CONFIG_ARM64_WORKAROUND_CLEAN_CACHE=y +CONFIG_ARM64_ERRATUM_826319=y +CONFIG_ARM64_ERRATUM_827319=y +CONFIG_ARM64_ERRATUM_824069=y +CONFIG_ARM64_ERRATUM_819472=y +CONFIG_ARM64_ERRATUM_832075=y +CONFIG_ARM64_ERRATUM_1742098=y +CONFIG_ARM64_ERRATUM_845719=y +CONFIG_ARM64_ERRATUM_843419=y +CONFIG_ARM64_ERRATUM_1024718=y +CONFIG_ARM64_ERRATUM_1418040=y +CONFIG_ARM64_WORKAROUND_SPECULATIVE_AT=y +CONFIG_ARM64_ERRATUM_1165522=y +CONFIG_ARM64_ERRATUM_1319367=y +CONFIG_ARM64_ERRATUM_1530923=y +CONFIG_ARM64_WORKAROUND_REPEAT_TLBI=y +CONFIG_ARM64_ERRATUM_1286807=y +CONFIG_ARM64_ERRATUM_1463225=y +CONFIG_ARM64_ERRATUM_1542419=y +CONFIG_ARM64_ERRATUM_1508412=y +CONFIG_ARM64_ERRATUM_2457168=y +CONFIG_CAVIUM_ERRATUM_22375=y +CONFIG_CAVIUM_ERRATUM_23154=y +CONFIG_CAVIUM_ERRATUM_27456=y +CONFIG_CAVIUM_ERRATUM_30115=y +CONFIG_CAVIUM_TX2_ERRATUM_219=y +CONFIG_FUJITSU_ERRATUM_010001=y +CONFIG_HISILICON_ERRATUM_161600802=y +CONFIG_QCOM_FALKOR_ERRATUM_1003=y +CONFIG_QCOM_FALKOR_ERRATUM_1009=y +CONFIG_QCOM_QDF2400_ERRATUM_0065=y +CONFIG_QCOM_FALKOR_ERRATUM_E1041=y +CONFIG_SOCIONEXT_SYNQUACER_PREITS=y +CONFIG_ARM64_4K_PAGES=y +# CONFIG_ARM64_16K_PAGES is not set +# CONFIG_ARM64_64K_PAGES is not set +# CONFIG_ARM64_VA_BITS_39 is not set +CONFIG_ARM64_VA_BITS=48 +CONFIG_ARM64_PA_BITS_48=y +CONFIG_ARM64_PA_BITS=48 +# CONFIG_CPU_BIG_ENDIAN is not set +CONFIG_CPU_LITTLE_ENDIAN=y +CONFIG_NR_CPUS=256 +CONFIG_HOTPLUG_CPU=y +# CONFIG_ARM64_SW_TTBR0_PAN is not set +CONFIG_ARM64_TAGGED_ADDR_ABI=y +CONFIG_KUSER_HELPERS=y +CONFIG_ARM64_HW_AFDBM=y +CONFIG_ARM64_PAN=y +CONFIG_AS_HAS_LSE_ATOMICS=y +CONFIG_ARM64_LSE_ATOMICS=y +CONFIG_ARM64_USE_LSE_ATOMICS=y +CONFIG_ARM64_RAS_EXTN=y +CONFIG_ARM64_CNP=y +CONFIG_ARM64_PTR_AUTH=y +CONFIG_CC_HAS_BRANCH_PROT_PAC_RET=y +CONFIG_CC_HAS_SIGN_RETURN_ADDRESS=y +CONFIG_AS_HAS_PAC=y +CONFIG_AS_HAS_CFI_NEGATE_RA_STATE=y +CONFIG_ARM64_AMU_EXTN=y +CONFIG_AS_HAS_ARMV8_4=y +CONFIG_ARM64_TLB_RANGE=y +CONFIG_ARM64_BTI=y +CONFIG_CC_HAS_BRANCH_PROT_PAC_RET_BTI=y +CONFIG_ARM64_E0PD=y +CONFIG_ARCH_RANDOM=y +CONFIG_ARM64_AS_HAS_MTE=y +CONFIG_ARM64_MTE=y +CONFIG_ARM64_MODULE_PLTS=y +# CONFIG_ARM64_PSEUDO_NMI is not set +CONFIG_RELOCATABLE=y +CONFIG_RANDOMIZE_MODULE_REGION_FULL=y +CONFIG_CC_HAVE_STACKPROTECTOR_SYSREG=y +CONFIG_BRIDGE=y +CONFIG_QRTR=y +CONFIG_QRTR_TUN=y +CONFIG_CAN=y +CONFIG_NFC=y +CONFIG_NFC_NCI=y +CONFIG_NFC_S3FWRN5_I2C=y +CONFIG_PCI_EPF_TEST=y +CONFIG_BLK_DEV_NBD=y +CONFIG_PCI_ENDPOINT_TEST=y +CONFIG_EEPROM_AT24=y +CONFIG_EEPROM_AT25=y +CONFIG_UACCE=y +CONFIG_SCSI_MPT3SAS=y +CONFIG_BLK_DEV_MD=y +CONFIG_DM_MIRROR=y +CONFIG_DM_ZERO=y +CONFIG_MACVLAN=y +CONFIG_MACVTAP=y +CONFIG_VETH=y +CONFIG_ATL1C=y +CONFIG_TOUCHSCREEN_ATMEL_MXT=y +CONFIG_IPMI_HANDLER=y +CONFIG_IPMI_DEVICE_INTERFACE=y +CONFIG_IPMI_SI=y +CONFIG_PHYTIUM_KCS_IPMI_BMC=y +CONFIG_PHYTIUM_BT_IPMI_BMC=y +CONFIG_I2C_GPIO=y +CONFIG_SPI_BITBANG=y +CONFIG_GPIO_ALTERA=y +CONFIG_GPIO_BD9571MWV=y +CONFIG_W1=y +CONFIG_W1_SLAVE_THERM=y +CONFIG_BATTERY_SBS=y +CONFIG_RC_CORE=y +CONFIG_MEDIA_SUPPORT=y +CONFIG_DRM_I2C_CH7006=y +CONFIG_DRM_I2C_SIL164=y +CONFIG_DRM_I2C_NXP_TDA998X=y +CONFIG_DRM_MALI_DISPLAY=y +CONFIG_DRM_RCAR_DW_HDMI=y +CONFIG_DRM_PANEL_TRULY_NT35597_WQXGA=y +CONFIG_DRM_DISPLAY_CONNECTOR=y +CONFIG_DRM_LONTIUM_LT9611=y +CONFIG_DRM_NWL_MIPI_DSI=y +CONFIG_DRM_SII902X=y +CONFIG_DRM_SIMPLE_BRIDGE=y +CONFIG_DRM_THINE_THC63LVD1024=y +CONFIG_DRM_TI_SN65DSI86=y +CONFIG_DRM_I2C_ADV7511=y +CONFIG_DRM_DW_HDMI_AHB_AUDIO=y +CONFIG_DRM_DW_HDMI_I2S_AUDIO=y +CONFIG_DRM_DW_HDMI_CEC=y +CONFIG_SND_SOC_FSL_SAI=y +CONFIG_SND_SOC_AK4613=y +CONFIG_SND_SOC_CROS_EC_CODEC=y +CONFIG_SND_SOC_DMIC=y +CONFIG_SND_SOC_ES7134=y +CONFIG_SND_SOC_ES7241=y +CONFIG_SND_SOC_MAX98357A=y +CONFIG_SND_SOC_MAX98927=y +CONFIG_SND_SOC_PCM3168A_I2C=y +CONFIG_SND_SOC_SIMPLE_AMPLIFIER=y +CONFIG_SND_SOC_SPDIF=y +CONFIG_SND_SOC_TAS571X=y +CONFIG_SND_SOC_WM8904=y +CONFIG_SND_SOC_WSA881X=y +CONFIG_I2C_HID=y +CONFIG_RTC_DRV_DS1307=y +CONFIG_RTC_DRV_RK808=y +CONFIG_RTC_DRV_PCF85363=y +CONFIG_RTC_DRV_RX8581=y +CONFIG_RTC_DRV_SD3068=y +CONFIG_RTC_DRV_PCF2127=y +CONFIG_CROS_EC_CHARDEV=y +CONFIG_COMMON_CLK_BD718XX=y +CONFIG_SOUNDWIRE=y +CONFIG_SOUNDWIRE_QCOM=y +CONFIG_MAX9611=y +CONFIG_QCOM_SPMI_ADC5=y +CONFIG_IIO_CROS_EC_SENSORS_CORE=y +CONFIG_IIO_CROS_EC_SENSORS=y +CONFIG_IIO_CROS_EC_LIGHT_PROX=y +CONFIG_SENSORS_ISL29018=y +CONFIG_IIO_CROS_EC_BARO=y +CONFIG_MPL3115=y +CONFIG_PWM_CROS_EC=y +CONFIG_PHY_MIXEL_MIPI_DPHY=y +CONFIG_FPGA_BRIDGE=y +CONFIG_ALTERA_FREEZE_BRIDGE=y +CONFIG_FPGA_REGION=y +CONFIG_OF_FPGA_REGION=y +CONFIG_OVERLAY_FS=y diff --git a/target/linux/phytium/armv8/target.mk b/target/linux/phytium/armv8/target.mk new file mode 100644 index 000000000..c5b20913f --- /dev/null +++ b/target/linux/phytium/armv8/target.mk @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Copyright (C) 2023-2024 Ailick <277498654@qq.com> + +ARCH:=aarch64 +SUBTARGET:=armv8 +BOARDNAME:=64-bit (armv8) SystemReady (EFI) compliant + +define Target/Description + Build multi-platform images for the Phytium ARMv8 instruction set architecture +endef diff --git a/target/linux/phytium/base-files.mk b/target/linux/phytium/base-files.mk new file mode 100644 index 000000000..88ba97d38 --- /dev/null +++ b/target/linux/phytium/base-files.mk @@ -0,0 +1,8 @@ +GRUB_SERIAL:=$(call qstrip,$(CONFIG_GRUB_SERIAL)) +ifeq ($(GRUB_SERIAL),) +$(error This platform requires CONFIG_GRUB_SERIAL be set!) +endif + +define Package/base-files/install-target + $(SED) "s#@GRUB_SERIAL@#$(GRUB_SERIAL)#" $(1)/etc/inittab +endef diff --git a/target/linux/phytium/base-files/etc/board.d/01_led b/target/linux/phytium/base-files/etc/board.d/01_led new file mode 100644 index 000000000..0250a9672 --- /dev/null +++ b/target/linux/phytium/base-files/etc/board.d/01_led @@ -0,0 +1,19 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-or-later + +. /lib/functions/uci-defaults.sh + +board_config_update + +board=$(board_name) + +case "$board" in +traverse,ten64) + ucidef_set_led_netdev "sfp1" "SFP 1" "ten64:green:sfp1:down" "eth8" "link tx rx" + ucidef_set_led_netdev "sfp2" "SFP 2" "ten64:green:sfp2:up" "eth9" "link tx rx" + ;; +esac + +board_config_flush + +exit 0 diff --git a/target/linux/phytium/base-files/etc/board.d/02_network b/target/linux/phytium/base-files/etc/board.d/02_network new file mode 100644 index 000000000..f58de1c27 --- /dev/null +++ b/target/linux/phytium/base-files/etc/board.d/02_network @@ -0,0 +1,18 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-or-later + +. /lib/functions/system.sh +. /lib/functions/uci-defaults.sh + +board_config_update + +case "$(board_name)" in + traverse,ten64) + ucidef_set_interface_lan "eth0 eth1 eth2 eth3" + ucidef_set_interface_wan "eth6" + ;; +esac + +board_config_flush + +exit 0 diff --git a/target/linux/phytium/base-files/etc/board.d/03_gpio_switches b/target/linux/phytium/base-files/etc/board.d/03_gpio_switches new file mode 100644 index 000000000..cf07bc0f5 --- /dev/null +++ b/target/linux/phytium/base-files/etc/board.d/03_gpio_switches @@ -0,0 +1,23 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-or-later + +. /lib/functions/uci-defaults.sh + +board_config_update + +board=$(board_name) + +case "$board" in +traverse,ten64) + ucidef_add_gpio_switch "lte_reset" "Cell Modem Reset" "376" + ucidef_add_gpio_switch "lte_power" "Cell Modem Power" "377" + ucidef_add_gpio_switch "lte_disable" "Cell Modem Airplane mode" "378" + ucidef_add_gpio_switch "gnss_disable" "Cell Modem Disable GNSS receiver" "379" + ucidef_add_gpio_switch "lower_sfp_txidsable" "Lower SFP+ TX Disable" "369" + ucidef_add_gpio_switch "upper_sfp_txdisable" "Upper SFP+ TX Disable" "373" + ;; +esac + +board_config_flush + +exit 0 diff --git a/target/linux/phytium/base-files/etc/inittab b/target/linux/phytium/base-files/etc/inittab new file mode 100644 index 000000000..87d5460d3 --- /dev/null +++ b/target/linux/phytium/base-files/etc/inittab @@ -0,0 +1,9 @@ +::sysinit:/etc/init.d/rcS S boot +::shutdown:/etc/init.d/rcS K shutdown +ttyAMA0::askfirst:/usr/libexec/login.sh +@GRUB_SERIAL@::askfirst:/usr/libexec/login.sh +tty0::askfirst:/usr/libexec/login.sh +hvc0::askfirst:/usr/libexec/login.sh +ttymxc0::askfirst:/usr/libexec/login.sh +ttymxc1::askfirst:/usr/libexec/login.sh +ttymxc2::askfirst:/usr/libexec/login.sh diff --git a/target/linux/phytium/base-files/lib/preinit/01_sysinfo_acpi b/target/linux/phytium/base-files/lib/preinit/01_sysinfo_acpi new file mode 100644 index 000000000..1069d74cf --- /dev/null +++ b/target/linux/phytium/base-files/lib/preinit/01_sysinfo_acpi @@ -0,0 +1,52 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +sanitize_name_arm64() { + sed -e ' + y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/; + s/[^a-z0-9_-]\+/-/g; + s/^-//; + s/-$//; + ' "$@" +} + +do_sysinfo_arm64() { + local vendor product file + + for file in sys_vendor board_vendor; do + vendor="$(cat /sys/devices/virtual/dmi/id/$file 2>/dev/null)" + case "$vendor" in + empty | \ + System\ manufacturer | \ + To\ [bB]e\ [fF]illed\ [bB]y\ O\.E\.M\.) + continue + ;; + esac + [ -n "$vendor" ] && break + done + + for file in product_name board_name; do + product="$(cat /sys/devices/virtual/dmi/id/$file 2>/dev/null)" + case "$vendor:$product" in + ?*:empty | \ + ?*:System\ Product\ Name | \ + ?*:To\ [bB]e\ [fF]illed\ [bB]y\ O\.E\.M\.) + continue + ;; + ?*:?*) + break + ;; + esac + done + + [ -d "/sys/firmware/devicetree/base" ] && return + + [ -n "$vendor" -a -n "$product" ] || return + + mkdir -p /tmp/sysinfo + + echo "$vendor $product" > /tmp/sysinfo/model + + sanitize_name_arm64 /tmp/sysinfo/model > /tmp/sysinfo/board_name +} + +boot_hook_add preinit_main do_sysinfo_arm64 diff --git a/target/linux/phytium/base-files/lib/preinit/79_move_config b/target/linux/phytium/base-files/lib/preinit/79_move_config new file mode 100644 index 000000000..864d4dfa6 --- /dev/null +++ b/target/linux/phytium/base-files/lib/preinit/79_move_config @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: GPL-2.0-only + +move_config() { + local partdev parttype=ext4 + + . /lib/upgrade/common.sh + + if export_bootdevice && export_partdevice partdev 1; then + part_magic_fat "/dev/$partdev" && parttype=vfat + if mount -t $parttype -o rw,noatime "/dev/$partdev" /mnt; then + if [ -f "/mnt/$BACKUP_FILE" ]; then + mv -f "/mnt/$BACKUP_FILE" / + fi + umount /mnt + fi + fi +} + +boot_hook_add preinit_mount_root move_config diff --git a/target/linux/phytium/base-files/lib/upgrade/platform.sh b/target/linux/phytium/base-files/lib/upgrade/platform.sh new file mode 100644 index 000000000..e72c6955e --- /dev/null +++ b/target/linux/phytium/base-files/lib/upgrade/platform.sh @@ -0,0 +1,165 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +RAMFS_COPY_BIN="/usr/sbin/blkid" + +platform_check_image() { + local board=$(board_name) + local diskdev partdev diff + [ "$#" -gt 1 ] && return 1 + + v "Board is ${board}" + + export_bootdevice && export_partdevice diskdev 0 || { + v "platform_check_image: Unable to determine upgrade device" + return 1 + } + + get_partitions "/dev/$diskdev" bootdisk + + v "Extract boot sector from the image" + get_image_dd "$1" of=/tmp/image.bs count=63 bs=512b + + get_partitions /tmp/image.bs image + + #compare tables + diff="$(grep -F -x -v -f /tmp/partmap.bootdisk /tmp/partmap.image)" + + rm -f /tmp/image.bs /tmp/partmap.bootdisk /tmp/partmap.image + + if [ -n "$diff" ]; then + v "Partition layout has changed. Full image will be written." + ask_bool 0 "Abort" && exit 1 + return 0 + fi +} + +platform_copy_config() { + local partdev parttype=ext4 + + if export_partdevice partdev 1; then + part_magic_fat "/dev/$partdev" && parttype=vfat + mount -t $parttype -o rw,noatime "/dev/$partdev" /mnt + cp -af "$UPGRADE_BACKUP" "/mnt/$BACKUP_FILE" + umount /mnt + else + v "ERROR: Unable to find partition to copy config data to" + fi + + sleep 5 +} + +# To avoid writing over any firmware +# files (e.g ubootefi.var or firmware/X/ aka EBBR) +# Copy efi/openwrt and efi/boot from the new image +# to the existing ESP +platform_do_upgrade_efi_system_partition() { + local image_file=$1 + local target_partdev=$2 + local image_efisp_start=$3 + local image_efisp_size=$4 + + v "Updating ESP on ${target_partdev}" + NEW_ESP_DIR="/mnt/new_esp_loop" + CUR_ESP_DIR="/mnt/cur_esp" + mkdir "${NEW_ESP_DIR}" + mkdir "${CUR_ESP_DIR}" + + get_image_dd "$image_file" of="/tmp/new_efi_sys_part.img" \ + skip="$image_efisp_start" count="$image_efisp_size" + + mount -t vfat -o loop -o ro /tmp/new_efi_sys_part.img "${NEW_ESP_DIR}" + if [ ! -d "${NEW_ESP_DIR}/efi/boot" ]; then + v "ERROR: Image does not contain EFI boot files (/efi/boot)" + return 1 + fi + + mount -t vfat "/dev/$partdev" "${CUR_ESP_DIR}" + + for d in $(find "${NEW_ESP_DIR}/efi/" -mindepth 1 -maxdepth 1 -type d); do + v "Copying ${d}" + newdir_bname=$(basename "${d}") + rm -rf "${CUR_ESP_DIR}/efi/${newdir_bname}" + cp -r "${d}" "${CUR_ESP_DIR}/efi" + done + + umount "${NEW_ESP_DIR}" + umount "${CUR_ESP_DIR}" +} + +platform_do_upgrade() { + local board=$(board_name) + local diskdev partdev diff + + export_bootdevice && export_partdevice diskdev 0 || { + v "platform_do_upgrade: Unable to determine upgrade device" + return 1 + } + + sync + + if [ "$UPGRADE_OPT_SAVE_PARTITIONS" = "1" ]; then + get_partitions "/dev/$diskdev" bootdisk + + v "Extract boot sector from the image" + get_image_dd "$1" of=/tmp/image.bs count=63 bs=512b + + get_partitions /tmp/image.bs image + + #compare tables + diff="$(grep -F -x -v -f /tmp/partmap.bootdisk /tmp/partmap.image)" + else + diff=1 + fi + + # Only change the partition table if sysupgrade -p is set, + # otherwise doing so could interfere with embedded "single storage" + # (e.g SoC boot from SD card) setups, as well as other user + # created storage (like uvol) + if [ -n "$diff" ] && [ "${UPGRADE_OPT_SAVE_PARTITIONS}" = "0" ]; then + # Need to remove partitions before dd, otherwise the partitions + # that are added after will have minor numbers offset + partx -d - "/dev/$diskdev" + + get_image_dd "$1" of="/dev/$diskdev" bs=4096 conv=fsync + + # Separate removal and addtion is necessary; otherwise, partition 1 + # will be missing if it overlaps with the old partition 2 + partx -a - "/dev/$diskdev" + + return 0 + fi + + #iterate over each partition from the image and write it to the boot disk + while read part start size; do + if export_partdevice partdev $part; then + v "Writing image to /dev/$partdev..." + if [ "$part" = "1" ]; then + platform_do_upgrade_efi_system_partition \ + $1 $partdev $start $size || return 1 + else + v "Normal partition, doing DD" + get_image_dd "$1" of="/dev/$partdev" ibs=512 obs=1M skip="$start" \ + count="$size" conv=fsync + fi + else + v "Unable to find partition $part device, skipped." + fi + done < /tmp/partmap.image + + local parttype=ext4 + + if (blkid > /dev/null) && export_partdevice partdev 1; then + part_magic_fat "/dev/$partdev" && parttype=vfat + mount -t $parttype -o rw,noatime "/dev/$partdev" /mnt + if export_partdevice partdev 2; then + THIS_PART_BLKID=$(blkid -o value -s PARTUUID "/dev/${partdev}") + v "Setting rootfs PARTUUID=${THIS_PART_BLKID}" + sed -i "s/\(PARTUUID=\)[a-f0-9-]\+/\1${THIS_PART_BLKID}/ig" \ + /mnt/efi/openwrt/grub.cfg + fi + umount /mnt + fi + # Provide time for the storage medium to flush before system reset + # (despite the sync/umount it appears NVMe etc. do it in the background) + sleep 5 +} diff --git a/target/linux/phytium/config-5.10 b/target/linux/phytium/config-5.10 new file mode 100644 index 000000000..73a54d5ef --- /dev/null +++ b/target/linux/phytium/config-5.10 @@ -0,0 +1,743 @@ +CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y +CONFIG_AUDIT=y +CONFIG_NO_HZ_IDLE=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_PREEMPT=y +CONFIG_IRQ_TIME_ACCOUNTING=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_BSD_PROCESS_ACCT_V3=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_NUMA_BALANCING=y +CONFIG_MEMCG=y +CONFIG_MEMCG_SWAP=y +CONFIG_BLK_CGROUP=y +CONFIG_CGROUP_PIDS=y +CONFIG_CGROUP_HUGETLB=y +CONFIG_CPUSETS=y +CONFIG_CGROUP_DEVICE=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_CGROUP_PERF=y +CONFIG_SCHED_AUTOGROUP=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_KALLSYMS_ALL=y +# CONFIG_COMPAT_BRK is not set +CONFIG_PROFILING=y +# CONFIG_ARCH_ACTIONS is not set +# CONFIG_ARCH_AGILEX is not set +# CONFIG_ARCH_SUNXI is not set +# CONFIG_ARCH_ALPINE is not set +# CONFIG_ARCH_BCM2835 is not set +# CONFIG_ARCH_BCM_IPROC is not set +# CONFIG_ARCH_BERLIN is not set +# CONFIG_ARCH_BRCMSTB is not set +# CONFIG_ARCH_EXYNOS is not set +# CONFIG_ARCH_K3 is not set +# CONFIG_ARCH_LAYERSCAPE is not set +# CONFIG_ARCH_LG1K is not set +# CONFIG_ARCH_HISI is not set +# CONFIG_ARCH_MEDIATEK is not set +# CONFIG_ARCH_MESON is not set +# CONFIG_ARCH_MVEBU is not set +# CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_QCOM is not set +# CONFIG_ARCH_RENESAS is not set +# CONFIG_ARCH_ROCKCHIP is not set +# CONFIG_ARCH_S32 is not set +# CONFIG_ARCH_SEATTLE is not set +# CONFIG_ARCH_STRATIX10 is not set +# CONFIG_ARCH_SYNQUACER is not set +# CONFIG_ARCH_TEGRA is not set +# CONFIG_ARCH_SPRD is not set +# CONFIG_ARCH_THUNDER is not set +# CONFIG_ARCH_THUNDER2 is not set +# CONFIG_ARCH_UNIPHIER is not set +# CONFIG_ARCH_VEXPRESS is not set +# CONFIG_ARCH_VISCONTI is not set +# CONFIG_ARCH_XGENE is not set +# CONFIG_ARCH_ZX is not set +# CONFIG_ARCH_ZYNQMP is not set +CONFIG_ARM64_VA_BITS_48=y +CONFIG_SCHED_MC=y +CONFIG_SCHED_SMT=y +CONFIG_NUMA=y +CONFIG_SECCOMP=y +CONFIG_KEXEC=y +CONFIG_KEXEC_FILE=y +CONFIG_CRASH_DUMP=y +CONFIG_XEN=y +CONFIG_COMPAT=y +CONFIG_RANDOMIZE_BASE=y +CONFIG_HIBERNATION=y +CONFIG_WQ_POWER_EFFICIENT_DEFAULT=y +CONFIG_ENERGY_MODEL=y +CONFIG_ARM_CPUIDLE=y +CONFIG_ARM_PSCI_CPUIDLE=y +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y +CONFIG_CPUFREQ_DT=y +CONFIG_ARM_ARMADA_37XX_CPUFREQ=y +CONFIG_ARM_SCPI_CPUFREQ=y +CONFIG_ARM_QCOM_CPUFREQ_NVMEM=y +CONFIG_ARM_QCOM_CPUFREQ_HW=y +CONFIG_ARM_TEGRA186_CPUFREQ=y +CONFIG_QORIQ_CPUFREQ=y +CONFIG_ARM_SCPI_PROTOCOL=y +CONFIG_RASPBERRYPI_FIRMWARE=y +CONFIG_INTEL_STRATIX10_SERVICE=y +CONFIG_EFI_CAPSULE_LOADER=y +CONFIG_IMX_SCU=y +CONFIG_IMX_SCU_PD=y +CONFIG_ARCH_SUPPORTS_ACPI=y +CONFIG_ACPI=y +CONFIG_ACPI_APEI=y +CONFIG_ACPI_APEI_GHES=y +CONFIG_ACPI_APEI_PCIEAER=y +CONFIG_ACPI_APEI_MEMORY_FAILURE=y +CONFIG_ACPI_APEI_EINJ=y +CONFIG_VIRTUALIZATION=y +CONFIG_KVM=y +CONFIG_ARM64_CRYPTO=y +CONFIG_CRYPTO_SHA1_ARM64_CE=y +CONFIG_CRYPTO_SHA2_ARM64_CE=y +CONFIG_CRYPTO_GHASH_ARM64_CE=y +CONFIG_CRYPTO_AES_ARM64_CE_CCM=y +CONFIG_CRYPTO_AES_ARM64_CE_BLK=y +CONFIG_JUMP_LABEL=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_KSM=y +CONFIG_MEMORY_FAILURE=y +CONFIG_TRANSPARENT_HUGEPAGE=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_NETFILTER=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_BRIDGE_VLAN_FILTERING=y +CONFIG_VLAN_8021Q_GVRP=y +CONFIG_VLAN_8021Q_MVRP=y +CONFIG_NET_SCHED=y +CONFIG_NET_CLS_ACT=y +CONFIG_BPF_JIT=y +# CONFIG_BT_HS is not set +# CONFIG_BT_LE is not set +CONFIG_BT_LEDS=y +# CONFIG_BT_DEBUGFS is not set +CONFIG_BT_HCIUART_LL=y +CONFIG_BT_HCIUART_BCM=y +CONFIG_BT_HCIUART_QCA=y +CONFIG_MAC80211_LEDS=y +CONFIG_NET_9P=y +CONFIG_NET_9P_VIRTIO=y +CONFIG_PCI=y +CONFIG_PCIEPORTBUS=y +CONFIG_PCI_IOV=y +CONFIG_PCI_PASID=y +CONFIG_HOTPLUG_PCI=y +CONFIG_HOTPLUG_PCI_ACPI=y +CONFIG_PCI_AARDVARK=y +CONFIG_PCI_TEGRA=y +CONFIG_PCIE_RCAR_HOST=y +CONFIG_PCIE_RCAR_EP=y +CONFIG_PCI_HOST_GENERIC=y +CONFIG_PCI_XGENE=y +CONFIG_PCIE_ALTERA=y +CONFIG_PCIE_ALTERA_MSI=y +CONFIG_PCI_HOST_THUNDER_PEM=y +CONFIG_PCI_HOST_THUNDER_ECAM=y +CONFIG_PCI_LAYERSCAPE=y +CONFIG_PCIE_LAYERSCAPE_GEN4=y +CONFIG_PCI_HISI=y +CONFIG_PCIE_QCOM=y +CONFIG_PCIE_ARMADA_8K=y +CONFIG_PCIE_KIRIN=y +CONFIG_PCIE_HISI_STB=y +CONFIG_PCI_ENDPOINT=y +CONFIG_PCI_ENDPOINT_CONFIGFS=y +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_FW_LOADER_USER_HELPER=y +CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y +CONFIG_HISILICON_LPC=y +CONFIG_SIMPLE_PM_BUS=y +CONFIG_FSL_MC_BUS=y +CONFIG_MTD=y +CONFIG_MTD_BLOCK=y +CONFIG_MTD_CFI=y +CONFIG_MTD_CFI_ADV_OPTIONS=y +CONFIG_MTD_CFI_INTELEXT=y +CONFIG_MTD_CFI_AMDSTD=y +CONFIG_MTD_CFI_STAA=y +CONFIG_MTD_PHYSMAP=y +CONFIG_MTD_PHYSMAP_OF=y +CONFIG_MTD_DATAFLASH=y +CONFIG_MTD_SST25L=y +CONFIG_MTD_RAW_NAND=y +CONFIG_MTD_NAND_DENALI_DT=y +CONFIG_MTD_NAND_MARVELL=y +CONFIG_MTD_NAND_FSL_IFC=y +CONFIG_MTD_NAND_QCOM=y +CONFIG_MTD_SPI_NOR=y +CONFIG_SPI_CADENCE_QUADSPI=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_VIRTIO_BLK=y +CONFIG_SRAM=y +# CONFIG_SCSI_PROC_FS is not set +CONFIG_BLK_DEV_SD=y +CONFIG_SCSI_SAS_ATA=y +CONFIG_SCSI_HISI_SAS=y +CONFIG_SCSI_HISI_SAS_PCI=y +CONFIG_MEGARAID_SAS=y +CONFIG_SCSI_UFSHCD=y +CONFIG_SCSI_UFSHCD_PLATFORM=y +CONFIG_SCSI_UFS_HISI=y +CONFIG_ATA=y +CONFIG_SATA_AHCI=y +CONFIG_SATA_AHCI_PLATFORM=y +CONFIG_AHCI_CEVA=y +CONFIG_AHCI_MVEBU=y +CONFIG_AHCI_XGENE=y +CONFIG_AHCI_QORIQ=y +CONFIG_SATA_SIL24=y +CONFIG_SATA_RCAR=y +CONFIG_PATA_PLATFORM=y +CONFIG_PATA_OF_PLATFORM=y +CONFIG_MD=y +CONFIG_NETDEVICES=y +CONFIG_TUN=y +CONFIG_VIRTIO_NET=y +CONFIG_AMD_XGBE=y +CONFIG_NET_XGENE=y +CONFIG_MACB=y +CONFIG_THUNDER_NIC_PF=y +CONFIG_FEC=y +CONFIG_FSL_FMAN=y +CONFIG_FSL_DPAA_ETH=y +CONFIG_FSL_DPAA2_ETH=y +CONFIG_FSL_ENETC=y +CONFIG_FSL_ENETC_VF=y +CONFIG_FSL_ENETC_QOS=y +CONFIG_HIX5HD2_GMAC=y +CONFIG_HNS_DSAF=y +CONFIG_HNS_ENET=y +CONFIG_HNS3=y +CONFIG_HNS3_HCLGE=y +CONFIG_HNS3_ENET=y +CONFIG_E1000=y +CONFIG_E1000E=y +CONFIG_IGB=y +CONFIG_IGBVF=y +CONFIG_MVNETA=y +CONFIG_MVPP2=y +CONFIG_SKY2=y +CONFIG_MLX5_CORE_EN=y +CONFIG_SH_ETH=y +CONFIG_RAVB=y +CONFIG_SMC91X=y +CONFIG_SMSC911X=y +CONFIG_SNI_AVE=y +CONFIG_SNI_NETSEC=y +CONFIG_TI_K3_AM65_CPSW_NUSS=y +CONFIG_MDIO_BUS_MUX_MMIOREG=y +CONFIG_MDIO_BUS_MUX_MULTIPLEXER=y +CONFIG_AQUANTIA_PHY=y +CONFIG_MICREL_PHY=y +CONFIG_MICROSEMI_PHY=y +CONFIG_AT803X_PHY=y +CONFIG_ROCKCHIP_PHY=y +CONFIG_VITESSE_PHY=y +CONFIG_INPUT_EVDEV=y +CONFIG_KEYBOARD_GPIO=y +CONFIG_KEYBOARD_CROS_EC=y +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_PM8941_PWRKEY=y +CONFIG_INPUT_HISI_POWERKEY=y +# CONFIG_SERIO_SERPORT is not set +CONFIG_SERIO_AMBAKMI=y +CONFIG_LEGACY_PTY_COUNT=16 +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_EXTENDED=y +CONFIG_SERIAL_8250_SHARE_IRQ=y +CONFIG_SERIAL_8250_BCM2835AUX=y +CONFIG_SERIAL_8250_DW=y +CONFIG_SERIAL_8250_OMAP=y +CONFIG_SERIAL_8250_MT6577=y +CONFIG_SERIAL_8250_UNIPHIER=y +CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SERIAL_AMBA_PL011=y +CONFIG_SERIAL_AMBA_PL011_CONSOLE=y +CONFIG_SERIAL_MESON=y +CONFIG_SERIAL_MESON_CONSOLE=y +CONFIG_SERIAL_SAMSUNG=y +CONFIG_SERIAL_SAMSUNG_CONSOLE=y +CONFIG_SERIAL_TEGRA=y +CONFIG_SERIAL_TEGRA_TCU=y +CONFIG_SERIAL_IMX=y +CONFIG_SERIAL_IMX_CONSOLE=y +CONFIG_SERIAL_SH_SCI=y +CONFIG_SERIAL_MSM=y +CONFIG_SERIAL_MSM_CONSOLE=y +CONFIG_SERIAL_QCOM_GENI=y +CONFIG_SERIAL_QCOM_GENI_CONSOLE=y +CONFIG_SERIAL_XILINX_PS_UART=y +CONFIG_SERIAL_XILINX_PS_UART_CONSOLE=y +CONFIG_SERIAL_FSL_LPUART=y +CONFIG_SERIAL_FSL_LPUART_CONSOLE=y +CONFIG_SERIAL_FSL_LINFLEXUART=y +CONFIG_SERIAL_FSL_LINFLEXUART_CONSOLE=y +CONFIG_SERIAL_MVEBU_UART=y +CONFIG_SERIAL_OWL=y +CONFIG_SERIAL_DEV_BUS=y +CONFIG_VIRTIO_CONSOLE=y +CONFIG_TCG_TPM=y +CONFIG_TCG_TIS_I2C_INFINEON=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_MUX=y +CONFIG_I2C_MUX_PCA954x=y +CONFIG_I2C_DESIGNWARE_PLATFORM=y +CONFIG_I2C_IMX=y +CONFIG_I2C_IMX_LPI2C=y +CONFIG_I2C_MESON=y +CONFIG_I2C_MV64XXX=y +CONFIG_I2C_OWL=y +CONFIG_I2C_PXA=y +CONFIG_I2C_QUP=y +CONFIG_I2C_RK3X=y +CONFIG_I2C_SH_MOBILE=y +CONFIG_I2C_TEGRA=y +CONFIG_I2C_UNIPHIER_F=y +CONFIG_I2C_RCAR=y +CONFIG_I2C_CROS_EC_TUNNEL=y +CONFIG_SPI=y +CONFIG_SPI_ARMADA_3700=y +CONFIG_SPI_FSL_LPSPI=y +CONFIG_SPI_FSL_QUADSPI=y +CONFIG_SPI_NXP_FLEXSPI=y +CONFIG_SPI_FSL_DSPI=y +CONFIG_SPI_ORION=y +CONFIG_SPI_PL022=y +CONFIG_SPI_ROCKCHIP=y +CONFIG_SPI_QUP=y +CONFIG_SPI_S3C64XX=y +CONFIG_SPI_SUN6I=y +CONFIG_SPMI=y +CONFIG_PINCTRL_SINGLE=y +CONFIG_PINCTRL_MAX77620=y +CONFIG_PINCTRL_OWL=y +CONFIG_PINCTRL_S700=y +CONFIG_PINCTRL_S900=y +CONFIG_PINCTRL_IMX8MM=y +CONFIG_PINCTRL_IMX8MN=y +CONFIG_PINCTRL_IMX8MP=y +CONFIG_PINCTRL_IMX8MQ=y +CONFIG_PINCTRL_IMX8QXP=y +CONFIG_PINCTRL_IMX8DXL=y +CONFIG_PINCTRL_IPQ8074=y +CONFIG_PINCTRL_IPQ6018=y +CONFIG_PINCTRL_MSM8916=y +CONFIG_PINCTRL_MSM8994=y +CONFIG_PINCTRL_MSM8996=y +CONFIG_PINCTRL_MSM8998=y +CONFIG_PINCTRL_QCS404=y +CONFIG_PINCTRL_QDF2XXX=y +CONFIG_PINCTRL_QCOM_SPMI_PMIC=y +CONFIG_PINCTRL_SC7180=y +CONFIG_PINCTRL_SDM845=y +CONFIG_PINCTRL_SM8150=y +CONFIG_PINCTRL_SM8250=y +CONFIG_GPIO_DWAPB=y +CONFIG_GPIO_MB86S7X=y +CONFIG_GPIO_MPC8XXX=y +CONFIG_GPIO_MXC=y +CONFIG_GPIO_PL061=y +CONFIG_GPIO_RCAR=y +CONFIG_GPIO_UNIPHIER=y +CONFIG_GPIO_XGENE=y +CONFIG_GPIO_XGENE_SB=y +CONFIG_GPIO_MAX732X=y +CONFIG_GPIO_PCA953X=y +CONFIG_GPIO_PCA953X_IRQ=y +CONFIG_GPIO_MAX77620=y +CONFIG_POWER_AVS=y +CONFIG_QCOM_CPR=y +CONFIG_ROCKCHIP_IODOMAIN=y +CONFIG_POWER_RESET_MSM=y +CONFIG_POWER_RESET_XGENE=y +CONFIG_POWER_RESET_SYSCON=y +CONFIG_SYSCON_REBOOT_MODE=y +CONFIG_BATTERY_BQ27XXX=y +CONFIG_SENSORS_ARM_SCPI=y +CONFIG_THERMAL_GOV_POWER_ALLOCATOR=y +CONFIG_CPU_THERMAL=y +CONFIG_THERMAL_EMULATION=y +CONFIG_SUN8I_THERMAL=y +CONFIG_RCAR_THERMAL=y +CONFIG_RCAR_GEN3_THERMAL=y +CONFIG_ARMADA_THERMAL=y +CONFIG_EXYNOS_THERMAL=y +CONFIG_QCOM_TSENS=y +CONFIG_UNIPHIER_THERMAL=y +CONFIG_WATCHDOG=y +CONFIG_ARM_SP805_WATCHDOG=y +CONFIG_ARM_SBSA_WATCHDOG=y +CONFIG_ARM_SMC_WATCHDOG=y +CONFIG_S3C2410_WATCHDOG=y +CONFIG_DW_WATCHDOG=y +CONFIG_IMX2_WDT=y +CONFIG_RENESAS_WDT=y +CONFIG_UNIPHIER_WATCHDOG=y +CONFIG_BCM2835_WDT=y +CONFIG_MFD_ALTERA_SYSMGR=y +CONFIG_MFD_BD9571MWV=y +CONFIG_MFD_AXP20X_I2C=y +CONFIG_MFD_AXP20X_RSB=y +CONFIG_MFD_HI6421_PMIC=y +CONFIG_MFD_HI655X_PMIC=y +CONFIG_MFD_MAX77620=y +CONFIG_MFD_SPMI_PMIC=y +CONFIG_MFD_RK808=y +CONFIG_MFD_SEC_CORE=y +CONFIG_MFD_SL28CPLD=y +CONFIG_MFD_ROHM_BD718XX=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +CONFIG_REGULATOR_AXP20X=y +CONFIG_REGULATOR_BD718XX=y +CONFIG_REGULATOR_BD9571MWV=y +CONFIG_REGULATOR_FAN53555=y +CONFIG_REGULATOR_GPIO=y +CONFIG_REGULATOR_HI6421V530=y +CONFIG_REGULATOR_HI655X=y +CONFIG_REGULATOR_MAX77620=y +CONFIG_REGULATOR_MAX8973=y +CONFIG_REGULATOR_PCA9450=y +CONFIG_REGULATOR_PFUZE100=y +CONFIG_REGULATOR_PWM=y +CONFIG_REGULATOR_QCOM_RPMH=y +CONFIG_REGULATOR_QCOM_SMD_RPM=y +CONFIG_REGULATOR_QCOM_SPMI=y +CONFIG_REGULATOR_RK808=y +CONFIG_REGULATOR_S2MPS11=y +CONFIG_RC_DECODERS=y +CONFIG_RC_DEVICES=y +CONFIG_MEDIA_CAMERA_SUPPORT=y +CONFIG_MEDIA_ANALOG_TV_SUPPORT=y +CONFIG_MEDIA_DIGITAL_TV_SUPPORT=y +CONFIG_MEDIA_SDR_SUPPORT=y +CONFIG_MEDIA_CONTROLLER=y +CONFIG_VIDEO_V4L2_SUBDEV_API=y +CONFIG_MEDIA_PLATFORM_SUPPORT=y +# CONFIG_DVB_NET is not set +CONFIG_MEDIA_USB_SUPPORT=y +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_V4L_MEM2MEM_DRIVERS=y +CONFIG_SDR_PLATFORM_DRIVERS=y +CONFIG_DRM_EXYNOS5433_DECON=y +CONFIG_DRM_EXYNOS7_DECON=y +CONFIG_DRM_EXYNOS_DSI=y +# CONFIG_DRM_EXYNOS_DP is not set +CONFIG_DRM_EXYNOS_HDMI=y +CONFIG_DRM_EXYNOS_MIC=y +CONFIG_ROCKCHIP_ANALOGIX_DP=y +CONFIG_ROCKCHIP_CDN_DP=y +CONFIG_ROCKCHIP_DW_HDMI=y +CONFIG_ROCKCHIP_DW_MIPI_DSI=y +CONFIG_ROCKCHIP_INNO_HDMI=y +CONFIG_DRM_I2C_ADV7511_AUDIO=y +CONFIG_FB=y +CONFIG_FB_MODE_HELPERS=y +CONFIG_FB_EFI=y +CONFIG_LOGO=y +# CONFIG_LOGO_LINUX_MONO is not set +# CONFIG_LOGO_LINUX_VGA16 is not set +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_SOC=y +CONFIG_SND_SOC_SAMSUNG=y +CONFIG_USB=y +CONFIG_USB_OTG=y +CONFIG_USB_XHCI_HCD=y +CONFIG_USB_XHCI_TEGRA=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_EXYNOS=y +CONFIG_USB_EHCI_HCD_PLATFORM=y +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_OHCI_EXYNOS=y +CONFIG_USB_OHCI_HCD_PLATFORM=y +CONFIG_USB_STORAGE=y +CONFIG_USB_MUSB_HDRC=y +CONFIG_USB_MUSB_SUNXI=y +CONFIG_USB_DWC3=y +CONFIG_USB_DWC2=y +CONFIG_USB_CHIPIDEA=y +CONFIG_USB_CHIPIDEA_UDC=y +CONFIG_USB_CHIPIDEA_HOST=y +CONFIG_USB_ISP1760=y +CONFIG_USB_HSIC_USB3503=y +CONFIG_NOP_USB_XCEIV=y +CONFIG_USB_GADGET=y +CONFIG_USB_CONFIGFS_SERIAL=y +CONFIG_USB_CONFIGFS_ACM=y +CONFIG_USB_CONFIGFS_OBEX=y +CONFIG_USB_CONFIGFS_NCM=y +CONFIG_USB_CONFIGFS_ECM=y +CONFIG_USB_CONFIGFS_ECM_SUBSET=y +CONFIG_USB_CONFIGFS_RNDIS=y +CONFIG_USB_CONFIGFS_EEM=y +CONFIG_USB_CONFIGFS_MASS_STORAGE=y +CONFIG_USB_CONFIGFS_F_FS=y +CONFIG_MMC=y +CONFIG_MMC_BLOCK_MINORS=32 +CONFIG_MMC_ARMMMCI=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_ACPI=y +CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_MMC_SDHCI_OF_ARASAN=y +CONFIG_MMC_SDHCI_OF_ESDHC=y +CONFIG_MMC_SDHCI_CADENCE=y +CONFIG_MMC_SDHCI_ESDHC_IMX=y +CONFIG_MMC_SDHCI_TEGRA=y +CONFIG_MMC_SDHCI_F_SDH30=y +CONFIG_MMC_MESON_GX=y +CONFIG_MMC_SDHCI_MSM=y +CONFIG_MMC_SPI=y +CONFIG_MMC_SDHI=y +CONFIG_MMC_UNIPHIER=y +CONFIG_MMC_DW=y +CONFIG_MMC_DW_EXYNOS=y +CONFIG_MMC_DW_HI3798CV200=y +CONFIG_MMC_DW_K3=y +CONFIG_MMC_DW_ROCKCHIP=y +CONFIG_MMC_SUNXI=y +CONFIG_MMC_BCM2835=y +CONFIG_MMC_SDHCI_XENON=y +CONFIG_MMC_SDHCI_AM654=y +CONFIG_MMC_OWL=y +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_PWM=y +CONFIG_LEDS_SYSCON=y +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_LEDS_TRIGGER_DISK=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +CONFIG_LEDS_TRIGGER_CPU=y +CONFIG_LEDS_TRIGGER_DEFAULT_ON=y +CONFIG_LEDS_TRIGGER_PANIC=y +CONFIG_EDAC=y +CONFIG_EDAC_GHES=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_MAX77686=y +CONFIG_RTC_DRV_S5M=y +CONFIG_RTC_DRV_DS3232=y +CONFIG_RTC_DRV_EFI=y +CONFIG_RTC_DRV_CROS_EC=y +CONFIG_RTC_DRV_S3C=y +CONFIG_RTC_DRV_PL031=y +CONFIG_RTC_DRV_SUN6I=y +CONFIG_RTC_DRV_ARMADA38X=y +CONFIG_RTC_DRV_TEGRA=y +CONFIG_RTC_DRV_XGENE=y +CONFIG_DMADEVICES=y +CONFIG_DMA_BCM2835=y +CONFIG_FSL_EDMA=y +CONFIG_K3_DMA=y +CONFIG_MV_XOR=y +CONFIG_MV_XOR_V2=y +CONFIG_OWL_DMA=y +CONFIG_PL330_DMA=y +CONFIG_TEGRA20_APB_DMA=y +CONFIG_QCOM_BAM_DMA=y +CONFIG_QCOM_HIDMA_MGMT=y +CONFIG_QCOM_HIDMA=y +CONFIG_RCAR_DMAC=y +CONFIG_TI_K3_UDMA=y +CONFIG_TI_K3_UDMA_GLUE_LAYER=y +CONFIG_VFIO=y +CONFIG_VFIO_PCI=y +CONFIG_VIRTIO_PCI=y +CONFIG_VIRTIO_BALLOON=y +CONFIG_VIRTIO_MMIO=y +CONFIG_XEN_GNTDEV=y +CONFIG_XEN_GRANT_DEV_ALLOC=y +CONFIG_MFD_CROS_EC_DEV=y +CONFIG_CHROME_PLATFORMS=y +CONFIG_CROS_EC=y +CONFIG_CROS_EC_I2C=y +CONFIG_CROS_EC_SPI=y +CONFIG_COMMON_CLK_RK808=y +CONFIG_COMMON_CLK_SCPI=y +CONFIG_COMMON_CLK_CS2000_CP=y +CONFIG_COMMON_CLK_FSL_SAI=y +CONFIG_COMMON_CLK_S2MPS11=y +CONFIG_COMMON_CLK_PWM=y +CONFIG_COMMON_CLK_VC5=y +CONFIG_CLK_IMX8MM=y +CONFIG_CLK_IMX8MN=y +CONFIG_CLK_IMX8MP=y +CONFIG_CLK_IMX8MQ=y +CONFIG_CLK_IMX8QXP=y +CONFIG_TI_SCI_CLK=y +CONFIG_COMMON_CLK_QCOM=y +CONFIG_QCOM_A53PLL=y +CONFIG_QCOM_CLK_APCS_MSM8916=y +CONFIG_QCOM_CLK_SMD_RPM=y +CONFIG_QCOM_CLK_RPMH=y +CONFIG_IPQ_GCC_8074=y +CONFIG_IPQ_GCC_6018=y +CONFIG_MSM_GCC_8916=y +CONFIG_MSM_GCC_8994=y +CONFIG_MSM_MMCC_8996=y +CONFIG_MSM_GCC_8998=y +CONFIG_QCS_GCC_404=y +CONFIG_SC_GCC_7180=y +CONFIG_SDM_GCC_845=y +CONFIG_SDM_GPUCC_845=y +CONFIG_SDM_VIDEOCC_845=y +CONFIG_SDM_DISPCC_845=y +CONFIG_SM_GCC_8150=y +CONFIG_SM_GCC_8250=y +CONFIG_SM_GPUCC_8150=y +CONFIG_SM_GPUCC_8250=y +CONFIG_QCOM_HFPLL=y +CONFIG_HWSPINLOCK=y +CONFIG_HWSPINLOCK_QCOM=y +CONFIG_ARM_MHU=y +CONFIG_IMX_MBOX=y +CONFIG_PLATFORM_MHU=y +CONFIG_BCM2835_MBOX=y +CONFIG_QCOM_APCS_IPC=y +CONFIG_QCOM_IPCC=y +CONFIG_ROCKCHIP_IOMMU=y +CONFIG_TEGRA_IOMMU_SMMU=y +CONFIG_ARM_SMMU=y +CONFIG_ARM_SMMU_V3=y +CONFIG_QCOM_IOMMU=y +CONFIG_REMOTEPROC=y +CONFIG_RPMSG_QCOM_GLINK_RPM=y +CONFIG_RPMSG_QCOM_SMD=y +CONFIG_OWL_PM_DOMAINS=y +CONFIG_RASPBERRYPI_POWER=y +CONFIG_FSL_DPAA=y +CONFIG_FSL_MC_DPIO=y +# CONFIG_QCOM_AOSS_QMP is not set +# CONFIG_QCOM_GENI_SE is not set +# CONFIG_QCOM_RPMH is not set +# CONFIG_QCOM_RPMHPD is not set +# CONFIG_QCOM_RPMPD is not set +# CONFIG_QCOM_SMEM is not set +# CONFIG_QCOM_SMD_RPM is not set +# CONFIG_QCOM_SMP2P is not set +# CONFIG_QCOM_SMSM is not set +# CONFIG_ARCH_R8A774A1 is not set +# CONFIG_ARCH_R8A774B1 is not set +# CONFIG_ARCH_R8A774C0 is not set +# CONFIG_ARCH_R8A774E1 is not set +# CONFIG_ARCH_R8A77950 is not set +# CONFIG_ARCH_R8A77951 is not set +# CONFIG_ARCH_R8A77960 is not set +# CONFIG_ARCH_R8A77961 is not set +# CONFIG_ARCH_R8A77965 is not set +# CONFIG_ARCH_R8A77970 is not set +# CONFIG_ARCH_R8A77980 is not set +# CONFIG_ARCH_R8A77990 is not set +# CONFIG_ARCH_R8A77995 is not set +# CONFIG_ROCKCHIP_PM_DOMAINS is not set +# CONFIG_ARCH_TEGRA_132_SOC is not set +# CONFIG_ARCH_TEGRA_210_SOC is not set +# CONFIG_ARCH_TEGRA_186_SOC is not set +# CONFIG_ARCH_TEGRA_194_SOC is not set +# CONFIG_ARCH_K3_AM6_SOC is not set +# CONFIG_ARCH_K3_J721E_SOC is not set +CONFIG_TI_SCI_PM_DOMAINS=y +CONFIG_EXTCON_USB_GPIO=y +CONFIG_EXTCON_USBC_CROS_EC=y +CONFIG_IIO=y +CONFIG_EXYNOS_ADC=y +CONFIG_PWM=y +CONFIG_PWM_ROCKCHIP=y +CONFIG_PWM_SAMSUNG=y +CONFIG_SL28CPLD_INTC=y +CONFIG_QCOM_PDC=y +CONFIG_RESET_IMX7=y +CONFIG_RESET_QCOM_AOSS=y +CONFIG_RESET_TI_SCI=y +CONFIG_PHY_XGENE=y +CONFIG_PHY_SUN4I_USB=y +CONFIG_PHY_HI6220_USB=y +CONFIG_PHY_HISTB_COMBPHY=y +CONFIG_PHY_HISI_INNO_USB2=y +CONFIG_PHY_MVEBU_CP110_COMPHY=y +CONFIG_PHY_QCOM_USB_HS=y +CONFIG_PHY_QCOM_USB_SNPS_FEMTO_V2=y +CONFIG_PHY_RCAR_GEN3_PCIE=y +CONFIG_PHY_RCAR_GEN3_USB2=y +CONFIG_PHY_ROCKCHIP_EMMC=y +CONFIG_PHY_ROCKCHIP_INNO_USB2=y +CONFIG_PHY_ROCKCHIP_TYPEC=y +CONFIG_PHY_UNIPHIER_USB2=y +CONFIG_PHY_UNIPHIER_USB3=y +CONFIG_PHY_TEGRA_XUSB=y +CONFIG_HISI_PMU=y +CONFIG_QCOM_L2_PMU=y +CONFIG_QCOM_L3_PMU=y +CONFIG_NVMEM_IMX_OCOTP=y +CONFIG_NVMEM_IMX_OCOTP_SCU=y +CONFIG_QCOM_QFPROM=y +CONFIG_ROCKCHIP_EFUSE=y +CONFIG_NVMEM_SUNXI_SID=y +CONFIG_UNIPHIER_EFUSE=y +CONFIG_FPGA=y +CONFIG_TEE=y +CONFIG_OPTEE=y +CONFIG_MUX_MMIO=y +CONFIG_INTERCONNECT=y +CONFIG_INTERCONNECT_QCOM=y +CONFIG_EXT2_FS=y +CONFIG_EXT3_FS=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_BTRFS_FS_POSIX_ACL=y +CONFIG_FANOTIFY=y +CONFIG_FANOTIFY_ACCESS_PERMISSIONS=y +CONFIG_QUOTA=y +CONFIG_AUTOFS4_FS=y +CONFIG_VFAT_FS=y +CONFIG_HUGETLBFS=y +CONFIG_CONFIGFS_FS=y +CONFIG_EFIVAR_FS=y +CONFIG_SQUASHFS=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=y +CONFIG_SECURITY=y +CONFIG_CRYPTO_ECHAINIV=y +CONFIG_CRYPTO_ANSI_CPRNG=y +CONFIG_CMA_SIZE_MBYTES=32 +CONFIG_PRINTK_TIME=y +CONFIG_DEBUG_INFO=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_FS=y +CONFIG_DEBUG_KERNEL=y +# CONFIG_SCHED_DEBUG is not set +# CONFIG_DEBUG_PREEMPT is not set +# CONFIG_FTRACE is not set +CONFIG_MEMTEST=y +# CONFIG_SHORTCUT_FE is not set +# CONFIG_RTL8821CS is not set diff --git a/target/linux/phytium/e2000/base-files/lib/upgrade/platform.sh b/target/linux/phytium/e2000/base-files/lib/upgrade/platform.sh new file mode 100755 index 000000000..cc16bda81 --- /dev/null +++ b/target/linux/phytium/e2000/base-files/lib/upgrade/platform.sh @@ -0,0 +1,35 @@ +PART_NAME=dtb:kernel:rootfs +REQUIRE_IMAGE_METADATA=1 + +platform_do_upgrade() { + local board=$(board_name) + + case "$board" in + e2000q-demo-board |\ + e2000d-demo-board |\ + e2000q-miniitx-board |\ + e2000d-miniitx-board) + default_do_upgrade "$1" + ;; + esac +} + +platform_check_image() { + local board=$(board_name) + local magic="$(get_magic_long "$1")" + + case "$board" in + e2000q-demo-board|\ + e2000d-demo-board|\ + e2000q-miniitx-board|\ + e2000d-miniitx-board) + [ "$magic" != "d00dfeed" ] && { + echo "Invalid image type." + return 1 + } + return 0 + ;; + esac + + return 0 +} diff --git a/target/linux/phytium/e2000/config-5.10 b/target/linux/phytium/e2000/config-5.10 new file mode 100644 index 000000000..b4a812628 --- /dev/null +++ b/target/linux/phytium/e2000/config-5.10 @@ -0,0 +1,604 @@ +CONFIG_64BIT=y +CONFIG_ARCH_DMA_ADDR_T_64BIT=y +CONFIG_ARCH_HIBERNATION_POSSIBLE=y +CONFIG_ARCH_KEEP_MEMBLOCK=y +CONFIG_ARCH_MMAP_RND_BITS=18 +CONFIG_ARCH_MMAP_RND_BITS_MAX=33 +CONFIG_ARCH_MMAP_RND_BITS_MIN=18 +CONFIG_ARCH_MMAP_RND_COMPAT_BITS=11 +CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MIN=11 +CONFIG_ARCH_PHYTIUM=y +CONFIG_ARCH_PROC_KCORE_TEXT=y +CONFIG_ARCH_SELECT_MEMORY_MODEL=y +CONFIG_ARCH_SPARSEMEM_DEFAULT=y +CONFIG_ARCH_SPARSEMEM_ENABLE=y +CONFIG_ARCH_STACKWALK=y +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARM64=y +CONFIG_ARM64_4K_PAGES=y +CONFIG_ARM64_CNP=y +CONFIG_ARM64_CRYPTO=y +CONFIG_ARM64_ERRATUM_1742098=y +CONFIG_ARM64_ERRATUM_819472=y +CONFIG_ARM64_ERRATUM_824069=y +CONFIG_ARM64_ERRATUM_826319=y +CONFIG_ARM64_ERRATUM_827319=y +CONFIG_ARM64_ERRATUM_832075=y +CONFIG_ARM64_ERRATUM_843419=y +CONFIG_ARM64_ERRATUM_845719=y +CONFIG_ARM64_ERRATUM_858921=y +CONFIG_ARM64_HW_AFDBM=y +CONFIG_ARM64_MODULE_PLTS=y +CONFIG_ARM64_PAGE_SHIFT=12 +CONFIG_ARM64_PAN=y +CONFIG_ARM64_PA_BITS=48 +CONFIG_ARM64_PA_BITS_48=y +CONFIG_ARM64_PTR_AUTH=y +CONFIG_ARM64_RAS_EXTN=y +CONFIG_ARM64_SVE=y +# CONFIG_ARM64_SW_TTBR0_PAN is not set +CONFIG_ARM64_TAGGED_ADDR_ABI=y +CONFIG_ARM64_UAO=y +CONFIG_ARM64_VA_BITS=48 +# CONFIG_ARM64_VA_BITS_39 is not set +CONFIG_ARM64_VA_BITS_48=y +CONFIG_ARM64_VHE=y +CONFIG_ARM64_WORKAROUND_CLEAN_CACHE=y +# CONFIG_ARMV8_DEPRECATED is not set +CONFIG_ARM_AMBA=y +CONFIG_ARM_ARCH_TIMER=y +CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y +CONFIG_ARM_ARCH_TIMER_OOL_WORKAROUND=y +CONFIG_ARM_CPUIDLE=y +CONFIG_ARM_GIC=y +CONFIG_ARM_GIC_V2M=y +CONFIG_ARM_GIC_V3=y +CONFIG_ARM_GIC_V3_ITS=y +CONFIG_ARM_GIC_V3_ITS_PCI=y +CONFIG_ARM_MHU=y +CONFIG_ARM_PSCI_CPUIDLE=y +CONFIG_ARM_PSCI_CPUIDLE_DOMAIN=y +CONFIG_ARM_PSCI_FW=y +CONFIG_ARM_SCMI_CPUFREQ=y +CONFIG_ARM_SCMI_POWER_DOMAIN=y +CONFIG_ARM_SCMI_PROTOCOL=y +CONFIG_ARM_SMMU=y +CONFIG_ARM_SMMU_DISABLE_BYPASS_BY_DEFAULT=y +# CONFIG_ARM_SMMU_LEGACY_DT_BINDINGS is not set +CONFIG_ARM_SMMU_V3=y +# CONFIG_ARM_SMMU_V3_SVA is not set +CONFIG_ATA=y +CONFIG_AUDIT_ARCH_COMPAT_GENERIC=y +CONFIG_BLK_DEV_BSG=y +CONFIG_BLK_DEV_BSGLIB=y +# CONFIG_BLK_DEV_INITRD is not set +CONFIG_BLK_DEV_INTEGRITY=y +CONFIG_BLK_DEV_INTEGRITY_T10=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_NVME=y +CONFIG_BLK_DEV_PCIESSD_MTIP32XX=y +CONFIG_BLK_DEV_SD=y +CONFIG_BLK_MQ_PCI=y +CONFIG_BLK_PM=y +CONFIG_BLK_SCSI_REQUEST=y +CONFIG_BLOCK_COMPAT=y +CONFIG_BRCMSTB_GISB_ARB=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_BSD_PROCESS_ACCT_V3=y +CONFIG_CC_HAVE_STACKPROTECTOR_SYSREG=y +CONFIG_CLKDEV_LOOKUP=y +CONFIG_CLONE_BACKWARDS=y +CONFIG_CMA=y +CONFIG_CMA_ALIGNMENT=8 +CONFIG_CMA_AREAS=7 +# CONFIG_CMA_DEBUG is not set +# CONFIG_CMA_DEBUGFS is not set +CONFIG_CMA_SIZE_MBYTES=5 +# CONFIG_CMA_SIZE_SEL_MAX is not set +CONFIG_CMA_SIZE_SEL_MBYTES=y +# CONFIG_CMA_SIZE_SEL_MIN is not set +# CONFIG_CMA_SIZE_SEL_PERCENTAGE is not set +CONFIG_COMMON_CLK=y +# CONFIG_COMMON_CLK_SCMI is not set +CONFIG_COMPAT=y +CONFIG_COMPAT_32BIT_TIME=y +CONFIG_COMPAT_BINFMT_ELF=y +CONFIG_COMPAT_NETLINK_MESSAGES=y +CONFIG_COMPAT_OLD_SIGACTION=y +CONFIG_CONFIGFS_FS=y +CONFIG_CONSOLE_TRANSLATIONS=y +CONFIG_CONTIG_ALLOC=y +# CONFIG_CPUFREQ_DT is not set +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 is not set +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +# CONFIG_CPU_FREQ_GOV_USERSPACE is not set +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_IDLE=y +CONFIG_CPU_IDLE_GOV_MENU=y +CONFIG_CPU_IDLE_MULTIPLE_DRIVERS=y +CONFIG_CPU_ISOLATION=y +CONFIG_CPU_PM=y +CONFIG_CPU_RMAP=y +CONFIG_CPU_THERMAL=y +CONFIG_CRASH_CORE=y +CONFIG_CRASH_DUMP=y +CONFIG_CRC16=y +# CONFIG_CRC32_SARWATE is not set +CONFIG_CRC32_SLICEBY8=y +CONFIG_CRC_T10DIF=y +CONFIG_CROSS_MEMORY_ATTACH=y +CONFIG_CRYPTO_AES_ARM64=y +CONFIG_CRYPTO_AES_ARM64_CE=y +CONFIG_CRYPTO_AES_ARM64_CE_BLK=y +CONFIG_CRYPTO_AES_ARM64_CE_CCM=y +CONFIG_CRYPTO_BLAKE2S=y +CONFIG_CRYPTO_CRC32=y +CONFIG_CRYPTO_CRC32C=y +CONFIG_CRYPTO_CRCT10DIF=y +CONFIG_CRYPTO_CRCT10DIF_ARM64_CE=y +CONFIG_CRYPTO_CRYPTD=y +CONFIG_CRYPTO_GHASH_ARM64_CE=y +CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_SIMD=y +CONFIG_DCACHE_WORD_ACCESS=y +CONFIG_DEBUG_BUGVERBOSE=y +# CONFIG_DEVFREQ_GOV_PASSIVE is not set +CONFIG_DEVFREQ_GOV_PERFORMANCE=y +CONFIG_DEVFREQ_GOV_POWERSAVE=y +CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND=y +CONFIG_DEVFREQ_GOV_USERSPACE=y +# CONFIG_DEVFREQ_THERMAL is not set +CONFIG_DEVMEM=y +# CONFIG_DEVPORT is not set +CONFIG_DMADEVICES=y +CONFIG_DMA_CMA=y +CONFIG_DMA_DIRECT_REMAP=y +CONFIG_DMA_ENGINE=y +CONFIG_DMA_OF=y +CONFIG_DMA_OPS=y +CONFIG_DMA_REMAP=y +CONFIG_DMA_SHARED_BUFFER=y +CONFIG_DNOTIFY=y +CONFIG_DTC=y +CONFIG_DT_IDLE_STATES=y +CONFIG_DUMMY_CONSOLE=y +CONFIG_DWMAC_DWC_QOS_ETH=y +CONFIG_DWMAC_GENERIC=y +CONFIG_E1000=y +CONFIG_E1000E=y +CONFIG_EDAC_SUPPORT=y +CONFIG_EEPROM_AT24=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_ENERGY_MODEL=y +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_EXTCON=y +CONFIG_F2FS_FS=y +CONFIG_FANOTIFY=y +CONFIG_FHANDLE=y +CONFIG_FIXED_PHY=y +CONFIG_FIX_EARLYCON_MEM=y +# CONFIG_FLATMEM_MANUAL is not set +# CONFIG_FORTIFY_SOURCE is not set +CONFIG_FRAME_POINTER=y +CONFIG_FRAME_WARN=2048 +CONFIG_FS_IOMAP=y +CONFIG_FS_MBCACHE=y +CONFIG_FS_POSIX_ACL=y +CONFIG_FW_LOADER_PAGED_BUF=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_CPU_AUTOPROBE=y +CONFIG_GENERIC_CPU_VULNERABILITIES=y +CONFIG_GENERIC_CSUM=y +CONFIG_GENERIC_EARLY_IOREMAP=y +CONFIG_GENERIC_GETTIMEOFDAY=y +CONFIG_GENERIC_IDLE_POLL_SETUP=y +CONFIG_GENERIC_IRQ_CHIP=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_MSI_IRQ=y +CONFIG_GENERIC_MSI_IRQ_DOMAIN=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_PHY=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=y +CONFIG_GPIOLIB_IRQCHIP=y +CONFIG_GPIO_DWAPB=y +CONFIG_GPIO_GENERIC=y +CONFIG_GPIO_GENERIC_PLATFORM=y +CONFIG_GPIO_PHYTIUM_CORE=y +# CONFIG_GPIO_PHYTIUM_PCI is not set +CONFIG_GPIO_PHYTIUM_PLAT=y +# CONFIG_GPIO_PHYTIUM_SGPIO is not set +CONFIG_HANDLE_DOMAIN_IRQ=y +# CONFIG_HARDENED_USERCOPY is not set +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_HAS_DMA=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT_MAP=y +CONFIG_HID=y +CONFIG_HID_GENERIC=y +CONFIG_HOLES_IN_ZONE=y +CONFIG_HOTPLUG_CPU=y +CONFIG_HOTPLUG_PCI=y +# CONFIG_HOTPLUG_PCI_CPCI is not set +# CONFIG_HOTPLUG_PCI_PCIE is not set +# CONFIG_HOTPLUG_PCI_SHPC is not set +CONFIG_HUGETLBFS=y +CONFIG_HUGETLB_PAGE=y +CONFIG_HWMON=y +CONFIG_HWSPINLOCK=y +CONFIG_HW_CONSOLE=y +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_COMPAT=y +CONFIG_I2C_HELPER_AUTO=y +CONFIG_ILLEGAL_POINTER_VALUE=0xdead000000000000 +CONFIG_INDIRECT_PIO=y +CONFIG_INPUT=y +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_FF_MEMLESS=y +CONFIG_INPUT_KEYBOARD=y +CONFIG_INPUT_LEDS=y +CONFIG_INPUT_MATRIXKMAP=y +# CONFIG_INPUT_MISC is not set +CONFIG_IOMMU_API=y +# CONFIG_IOMMU_DEBUGFS is not set +# CONFIG_IOMMU_DEFAULT_PASSTHROUGH is not set +CONFIG_IOMMU_DMA=y +CONFIG_IOMMU_IOVA=y +CONFIG_IOMMU_IO_PGTABLE=y +# CONFIG_IOMMU_IO_PGTABLE_ARMV7S is not set +CONFIG_IOMMU_IO_PGTABLE_LPAE=y +# CONFIG_IOMMU_IO_PGTABLE_LPAE_SELFTEST is not set +CONFIG_IOMMU_SUPPORT=y +# CONFIG_IO_STRICT_DEVMEM is not set +CONFIG_IO_URING=y +CONFIG_IRQCHIP=y +CONFIG_IRQ_DOMAIN=y +CONFIG_IRQ_DOMAIN_HIERARCHY=y +CONFIG_IRQ_FORCED_THREADING=y +CONFIG_IRQ_MSI_IOMMU=y +CONFIG_IRQ_TIME_ACCOUNTING=y +CONFIG_IRQ_WORK=y +CONFIG_JBD2=y +CONFIG_JFFS2_ZLIB=y +CONFIG_JUMP_LABEL=y +CONFIG_KALLSYMS=y +CONFIG_KEXEC_CORE=y +CONFIG_KEXEC_FILE=y +CONFIG_KSM=y +# CONFIG_LEDS_BRIGHTNESS_HW_CHANGED is not set +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_PWM=y +CONFIG_LEDS_SYSCON=y +CONFIG_LEDS_TRIGGER_CPU=y +CONFIG_LEDS_TRIGGER_PANIC=y +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=16 +CONFIG_LIBCRC32C=y +CONFIG_LIBFDT=y +CONFIG_LOCALVERSION_AUTO=y +CONFIG_LOCK_DEBUGGING_SUPPORT=y +CONFIG_LOCK_SPIN_ON_OWNER=y +CONFIG_LOG_BUF_SHIFT=19 +CONFIG_MACB=y +# CONFIG_MACB_PCI is not set +CONFIG_MAGIC_SYSRQ=y +CONFIG_MAGIC_SYSRQ_SERIAL=y +CONFIG_MAILBOX=y +# CONFIG_MAILBOX_TEST is not set +CONFIG_MANDATORY_FILE_LOCKING=y +CONFIG_MDIO_BUS=y +CONFIG_MDIO_BUS_MUX=y +CONFIG_MDIO_BUS_MUX_GPIO=y +CONFIG_MDIO_BUS_MUX_MMIOREG=y +CONFIG_MDIO_DEVICE=y +CONFIG_MDIO_DEVRES=y +CONFIG_MEMFD_CREATE=y +CONFIG_MEMORY_ISOLATION=y +CONFIG_MFD_SYSCON=y +CONFIG_MIGRATION=y +CONFIG_MMC=y +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_MINORS=32 +CONFIG_MMC_CQHCI=y +# CONFIG_MMC_PHYTIUM_MCI_PLTFM is not set +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_OF_ARASAN=y +CONFIG_MMC_SDHCI_OF_DWCMSHC=y +# CONFIG_MMC_SDHCI_PCI is not set +CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_MODULES_USE_ELF_RELA=y +CONFIG_MOTORCOMM_PHY=y +CONFIG_MQ_IOSCHED_DEADLINE=y +# CONFIG_MTD_CFI is not set +CONFIG_MTD_CMDLINE_PARTS=y +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +CONFIG_MTD_SPI_NOR=y +CONFIG_MTD_SPI_NOR_USE_4K_SECTORS=y +CONFIG_MTD_SPLIT_FIRMWARE=y +CONFIG_MUTEX_SPIN_ON_OWNER=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_NEED_SG_DMA_LENGTH=y +CONFIG_NET_FLOW_LIMIT=y +CONFIG_NLS=y +CONFIG_NLS_ISO8859_1=y +CONFIG_NOP_USB_XCEIV=y +CONFIG_NO_HZ_COMMON=y +CONFIG_NO_HZ_IDLE=y +CONFIG_NR_CPUS=256 +CONFIG_NVMEM=y +CONFIG_NVMEM_SYSFS=y +CONFIG_NVME_CORE=y +CONFIG_NVME_HWMON=y +CONFIG_NVME_MULTIPATH=y +CONFIG_OF=y +CONFIG_OF_ADDRESS=y +CONFIG_OF_DYNAMIC=y +CONFIG_OF_EARLY_FLATTREE=y +CONFIG_OF_FLATTREE=y +CONFIG_OF_GPIO=y +CONFIG_OF_IOMMU=y +CONFIG_OF_IRQ=y +CONFIG_OF_KOBJ=y +CONFIG_OF_MDIO=y +CONFIG_OF_NET=y +CONFIG_OF_OVERLAY=y +CONFIG_OF_RESOLVE=y +CONFIG_OLD_SIGSUSPEND3=y +# CONFIG_OVERLAY_FS_XINO_AUTO is not set +CONFIG_PADATA=y +CONFIG_PAGE_POOL=y +# CONFIG_PANIC_ON_OOPS is not set +CONFIG_PANIC_ON_OOPS_VALUE=0 +CONFIG_PANIC_TIMEOUT=0 +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_PARTITION_PERCPU=y +CONFIG_PCI=y +CONFIG_PCIEAER=y +CONFIG_PCIEASPM=y +CONFIG_PCIEASPM_DEFAULT=y +# CONFIG_PCIEASPM_PERFORMANCE is not set +# CONFIG_PCIEASPM_POWERSAVE is not set +# CONFIG_PCIEASPM_POWER_SUPERSAVE is not set +CONFIG_PCIEPORTBUS=y +CONFIG_PCIE_PME=y +CONFIG_PCI_DOMAINS=y +CONFIG_PCI_DOMAINS_GENERIC=y +CONFIG_PCI_ECAM=y +CONFIG_PCI_HOST_COMMON=y +CONFIG_PCI_HOST_GENERIC=y +CONFIG_PCI_MSI=y +CONFIG_PCI_MSI_IRQ_DOMAIN=y +CONFIG_PCI_STUB=y +CONFIG_PCS_XPCS=y +CONFIG_PGTABLE_LEVELS=4 +CONFIG_PHYLIB=y +CONFIG_PHYLINK=y +CONFIG_PHYS_ADDR_T_64BIT=y +CONFIG_PHYTIUM_MBOX=y +CONFIG_PINCTRL=y +# CONFIG_PINCTRL_SINGLE is not set +CONFIG_PLATFORM_MHU=y +CONFIG_PM=y +CONFIG_PM_CLK=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_POWER_RESET=y +CONFIG_POWER_SUPPLY=y +CONFIG_POWER_SUPPLY_HWMON=y +CONFIG_PREEMPT=y +CONFIG_PREEMPTION=y +CONFIG_PREEMPT_COUNT=y +# CONFIG_PREEMPT_NONE is not set +CONFIG_PREEMPT_RCU=y +CONFIG_PRINTK_TIME=y +# CONFIG_PRINT_QUOTA_WARNING is not set +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_PROC_VMCORE=y +CONFIG_PWM=y +CONFIG_PWM_SYSFS=y +# CONFIG_QFMT_V1 is not set +# CONFIG_QFMT_V2 is not set +CONFIG_QUEUED_RWLOCKS=y +CONFIG_QUEUED_SPINLOCKS=y +CONFIG_QUOTA=y +CONFIG_QUOTACTL=y +# CONFIG_QUOTA_NETLINK_INTERFACE is not set +CONFIG_RAID_ATTRS=y +CONFIG_RANDOMIZE_BASE=y +CONFIG_RANDOMIZE_MODULE_REGION_FULL=y +CONFIG_RAS=y +CONFIG_RATIONAL=y +# CONFIG_RAVE_SP_CORE is not set +CONFIG_RCU_TRACE=y +CONFIG_REALTEK_PHY=y +CONFIG_REGMAP=y +CONFIG_REGMAP_I2C=y +CONFIG_REGMAP_MMIO=y +CONFIG_REGULATOR=y +CONFIG_REGULATOR_FAN53555=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +CONFIG_REGULATOR_GPIO=y +CONFIG_REGULATOR_PWM=y +CONFIG_RELOCATABLE=y +CONFIG_RESET_CONTROLLER=y +CONFIG_RESET_SCMI=y +CONFIG_RFS_ACCEL=y +CONFIG_RODATA_FULL_DEFAULT_ENABLED=y +CONFIG_RPS=y +CONFIG_RSEQ=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_I2C_AND_SPI=y +CONFIG_RTC_NVMEM=y +# CONFIG_RTL8821CS is not set +# CONFIG_RUNTIME_TESTING_MENU is not set +CONFIG_RWSEM_SPIN_ON_OWNER=y +CONFIG_SATA_AHCI=y +CONFIG_SATA_AHCI_PLATFORM=y +CONFIG_SATA_HOST=y +CONFIG_SCHED_MC=y +CONFIG_SCSI=y +# CONFIG_SCSI_LOWLEVEL is not set +# CONFIG_SCSI_PROC_FS is not set +# CONFIG_SCSI_SAS_ATA is not set +CONFIG_SCSI_SAS_ATTRS=y +CONFIG_SCSI_SAS_HOST_SMP=y +CONFIG_SCSI_SAS_LIBSAS=y +# CONFIG_SECURITY_DMESG_RESTRICT is not set +CONFIG_SENSORS_ARM_SCMI=y +CONFIG_SERIAL_8250_DEPRECATED_OPTIONS=y +CONFIG_SERIAL_8250_DW=y +CONFIG_SERIAL_8250_DWLIB=y +CONFIG_SERIAL_8250_EXAR=y +CONFIG_SERIAL_8250_EXTENDED=y +CONFIG_SERIAL_8250_FSL=y +CONFIG_SERIAL_8250_NR_UARTS=4 +CONFIG_SERIAL_8250_PCI=y +CONFIG_SERIAL_8250_RUNTIME_UARTS=4 +CONFIG_SERIAL_8250_SHARE_IRQ=y +CONFIG_SERIAL_AMBA_PL011=y +CONFIG_SERIAL_AMBA_PL011_CONSOLE=y +CONFIG_SERIAL_DEV_BUS=y +CONFIG_SERIAL_DEV_CTRL_TTYPORT=y +CONFIG_SERIAL_MCTRL_GPIO=y +CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SERIO=y +CONFIG_SERIO_AMBAKMI=y +CONFIG_SERIO_LIBPS2=y +CONFIG_SG_POOL=y +CONFIG_SIMPLE_PM_BUS=y +CONFIG_SLUB_DEBUG=y +CONFIG_SMP=y +CONFIG_SPARSEMEM=y +CONFIG_SPARSEMEM_EXTREME=y +CONFIG_SPARSEMEM_MANUAL=y +CONFIG_SPARSEMEM_VMEMMAP=y +CONFIG_SPARSEMEM_VMEMMAP_ENABLE=y +CONFIG_SPARSE_IRQ=y +CONFIG_SPI=y +CONFIG_SPI_BITBANG=y +CONFIG_SPI_DYNAMIC=y +CONFIG_SPI_MASTER=y +CONFIG_SPI_MEM=y +CONFIG_SPI_PHYTIUM=y +# CONFIG_SPI_PHYTIUM_PCI is not set +CONFIG_SPI_PHYTIUM_PLAT=y +CONFIG_SPI_PHYTIUM_QSPI=y +CONFIG_SPI_PHYTIUM_QUADSPI=y +CONFIG_SPI_SPIDEV=y +# CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU is not set +CONFIG_SQUASHFS_DECOMP_SINGLE=y +# CONFIG_SQUASHFS_EMBEDDED is not set +CONFIG_SQUASHFS_FILE_CACHE=y +# CONFIG_SQUASHFS_FILE_DIRECT is not set +CONFIG_SRAM=y +CONFIG_SRCU=y +CONFIG_STACKPROTECTOR=y +CONFIG_STACKPROTECTOR_PER_TASK=y +CONFIG_STACKPROTECTOR_STRONG=y +CONFIG_STMMAC_ETH=y +CONFIG_STMMAC_PLATFORM=y +# CONFIG_STMMAC_SELFTESTS is not set +CONFIG_STRICT_DEVMEM=y +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_SWAP is not set +CONFIG_SWIOTLB=y +CONFIG_SWPHY=y +CONFIG_SYNC_FILE=y +CONFIG_SYSCTL_EXCEPTION_TRACE=y +CONFIG_SYSFS_SYSCALL=y +CONFIG_SYSVIPC_COMPAT=y +CONFIG_SYS_SUPPORTS_HUGETLBFS=y +# CONFIG_TEXTSEARCH is not set +CONFIG_THERMAL=y +CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y +CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS=0 +CONFIG_THERMAL_EMULATION=y +CONFIG_THERMAL_GOV_POWER_ALLOCATOR=y +CONFIG_THERMAL_GOV_STEP_WISE=y +CONFIG_THERMAL_HWMON=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_TRACE_CLOCK=y +CONFIG_TRANSPARENT_HUGEPAGE=y +CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y +# CONFIG_TRANSPARENT_HUGEPAGE_MADVISE is not set +CONFIG_TREE_RCU=y +CONFIG_TREE_SRCU=y +CONFIG_TYPEC=y +# CONFIG_TYPEC_DP_ALTMODE is not set +CONFIG_TYPEC_FUSB302=y +# CONFIG_TYPEC_HD3SS3220 is not set +# CONFIG_TYPEC_MUX_PI3USB30532 is not set +# CONFIG_TYPEC_STUSB160X is not set +# CONFIG_TYPEC_TCPCI is not set +CONFIG_TYPEC_TCPM=y +# CONFIG_TYPEC_TPS6598X is not set +# CONFIG_UACCE is not set +# CONFIG_UEVENT_HELPER is not set +CONFIG_UNINLINE_SPIN_UNLOCK=y +CONFIG_UNMAP_KERNEL_AT_EL0=y +CONFIG_USB=y +CONFIG_USB_COMMON=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_HCD_PLATFORM=y +# CONFIG_USB_EHCI_ROOT_HUB_TT is not set +CONFIG_USB_GADGET=y +CONFIG_USB_HID=y +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_OHCI_HCD_PLATFORM=y +CONFIG_USB_PHY=y +CONFIG_USB_PHYTIUM=y +CONFIG_USB_ROLE_SWITCH=y +CONFIG_USB_STORAGE=y +CONFIG_USB_SUPPORT=y +CONFIG_USB_ULPI=y +CONFIG_USB_ULPI_BUS=y +CONFIG_USB_ULPI_VIEWPORT=y +CONFIG_USB_XHCI_HCD=y +CONFIG_USB_XHCI_PLATFORM=y +# CONFIG_VIRTIO_MENU is not set +CONFIG_VMAP_STACK=y +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_VT_HW_CONSOLE_BINDING=y +# CONFIG_WATCHDOG is not set +CONFIG_XARRAY_MULTI=y +CONFIG_XPS=y +CONFIG_XXHASH=y +CONFIG_XZ_DEC_ARM=y +CONFIG_XZ_DEC_ARMTHUMB=y +CONFIG_XZ_DEC_BCJ=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZONE_DMA32=y diff --git a/target/linux/phytium/e2000/target.mk b/target/linux/phytium/e2000/target.mk new file mode 100644 index 000000000..3f1d89d92 --- /dev/null +++ b/target/linux/phytium/e2000/target.mk @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Copyright (c) 2023 Phytium Technology Co., Ltd. + +ARCH:=aarch64 +SUBTARGET:=e2000 +BOARDNAME:=E2000 + +define Target/Description + Build firmware image for Phytium E2000 devices. +endef diff --git a/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/dma/phytium-ddma.yaml b/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/dma/phytium-ddma.yaml new file mode 100644 index 000000000..7dbe7c44f --- /dev/null +++ b/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/dma/phytium-ddma.yaml @@ -0,0 +1,55 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +title: Phytium DDMA Controller bindings + +description: + The Phytium DDMA is a general-purpose direct memory access controller capable of + supporting 8 independent DMA channels. Each channel can have up to 32 requests. + DMA clients connected to the Phytium DDMA controller must use the format + described in the dma.txt file, using a two-cell specifier for each + channel: a phandle to the DMA controller plus the following two integer cells: + 1. The channel id + 2. The request line number + +maintainers: + - Huang Jie + +allOf: + - $ref: "dma-controller.yaml#" + +properties: + "#dma-cells": + const: 2 + + compatible: + const: phytium,ddma + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + dma-channels: + minItems: 1 + maxItems: 8 + description: it indicates that the number of channels are used + +required: + - compatible + - reg + - interrupts + - dma-channels + +unevaluatedProperties: false + +examples: + ddma0: ddma@28003000 { + compatible = "phytium,ddma"; + reg = <0x0 0x28003000 0x0 0x1000>; + interrupts = ; + #dma-cells = <2>; + dma-channels = <8>; + }; +... diff --git a/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/edac/phytium-pe220x-edac.txt b/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/edac/phytium-pe220x-edac.txt new file mode 100644 index 000000000..ddbf14b01 --- /dev/null +++ b/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/edac/phytium-pe220x-edac.txt @@ -0,0 +1,23 @@ +* Phytium Pe220x SoC EDAC node + +EDAC node is defined to describe on-chip error detection and correction. +The follow error types are supported: + + SoC - SoC IP's such as Ethernet, SATA, and etc + +The following section describes the EDAC DT node binding. + +Required properties: +- compatible: Shall be "phytium,pe220x-edac". +- reg: Shall be the Pe220x RAS resource. +- interrupts: Interrupt-specifier for RAS error IRQ(s). + +Example: + edac: edac@32b28000 { + compatible = "phytium,pe220x-edac"; + reg = <0x0 0x32b28000 0x0 0x1000>, + <0x0 0x31400000 0x0 0x1000>, + <0x0 0x31401000 0x0 0x1000>; + interrupts = , + , + }; diff --git a/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/gpio/gpio-phytium-sgpio.txt b/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/gpio/gpio-phytium-sgpio.txt new file mode 100644 index 000000000..65c5c1b9b --- /dev/null +++ b/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/gpio/gpio-phytium-sgpio.txt @@ -0,0 +1,32 @@ +* Phytium SGPIO controller + +This SGPIO controller is for Phytium Pe220x SoC, which supports up to +96 (32x3) Serial GPIOs. + +Required properties: +- compatible : Should contain "phytium,gpio" +- reg : Address and length of the register set for the device. +- interrupts: Interrupt mapping for GPIO IRQ. +- gpio-controller : Marks the device node as a gpio controller. +- #gpio-cells : Should be 2. The first cell is the pin number and + the second cell is used to specify the gpio polarity: + 0 = active high + 1 = active low +- ngpios: number of GPIO lines, see gpio.txt + (should be multiple of 32, up to 96 pins) +- bus-frequency: SGPIO CLK frequency +- clocks: A phandle to the APB clock for SGPIO clock division + +Example: + + sgpio: sgpio@2807d000 { + compatible = "phytium,sgpio"; + reg = <0x0 0x2807d000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_48mhz>; + ngpios = <96>; + bus-frequency = <48000>; + gpio-controller; + #gpio-cells = <2>; + }; + diff --git a/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/gpio/phytium,gpio.yaml b/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/gpio/phytium,gpio.yaml new file mode 100755 index 000000000..0a6dfd408 --- /dev/null +++ b/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/gpio/phytium,gpio.yaml @@ -0,0 +1,113 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/phytium,gpio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Phytium GPIO controller + +description: | + Phytium GPIO controllers have one or two configurable ports, each of which + are intended to be represented as child nodes with the generic GPIO-controller + properties as desribed in this bindings file. + +maintainers: + - Chen Baozi + +properties: + $nodename: + pattern: "^gpio@[0-9a-f]+$" + + compatible: + const: phytium,gpio + + reg: + maxItems: 1 + + gpio-controller: true + + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + + '#gpio-cells': + const: 2 + + interrupts: + description: | + The interrupts to the parent controller raised when GPIOs generate + the interrupts. If the controller provides one combined interrupt + for all GPIOs, specify a single interrupt. If the controller provides + one interrupt for each GPIO, provide a list of interrupts that + correspond to each of the GPIO pins. + minItems: 1 + maxItems: 32 + + interrupt-controller: true + + '#interrupt-cells': + const: 2 + +patternProperties: + "^gpio-(port|controller)@[0-9a-f]+$": + type: object + properties: + compatible: + const: phytium,gpio-port + + reg: + maxItems: 1 + + nr-gpios: + description: The number of GPIO pins exported by the port. + default: 32 + minimum: 1 + maximum: 32 + + required: + - compatible + - reg + - gpio-controller + - '#gpio-cells' + + dependencies: + interrupt-controller: [ interrupts ] + + additionalProperties: false + +additionalProperties: false + +required: + - compatible + - reg + - "#address-cells" + - "#size-cells" + +examples: + - | + gpio: gpio@28004000 { + compatible = "phytium,gpio"; + reg = <0x0 0x28004000 0x0 0x1000>; + gpio-controller; + #gpio-cells = <2>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = ; + interrupt-controller; + #interrupt-cells = <2>; + + porta: gpio-port@0 { + compatible = "phytium,gpio-port"; + reg = <0>; + nr-gpios = <8>; + }; + + portb: gpio-port@1 { + compatible = "phytium,gpio-port"; + reg = <1>; + nr-gpios = <8>; + }; +}; +... diff --git a/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/hwlock/phytium-hwspinlock.txt b/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/hwlock/phytium-hwspinlock.txt new file mode 100644 index 000000000..f39c86492 --- /dev/null +++ b/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/hwlock/phytium-hwspinlock.txt @@ -0,0 +1,21 @@ +Phytium HwSpinlock Driver +======================== + +Required properties: +- compatible: Should be "phytium,hwspinlock" +- reg: Contains the hwspinlock module register address space + (base address and length) +- #hwlock-cells: Should be 1. +- nr-locks: The number of locks in the device. + +Please look at the generic hwlock binding for usage information for consumers, +"Documentation/devicetree/bindings/hwlock/hwlock.txt" + +Example: + +hwspinlock: spinlock@40000000 { + compatible = "phytium,hwspinlock"; + reg = <0x40000000 0x1000>; + #hwlock-cells = <1>; + nr-locks = <32>; +}; diff --git a/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/hwmon/tacho-phytium.txt b/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/hwmon/tacho-phytium.txt new file mode 100755 index 000000000..0d02b0684 --- /dev/null +++ b/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/hwmon/tacho-phytium.txt @@ -0,0 +1,48 @@ +Phytium Fan Tacho and capture counter controller device driver + +The controller can support one input signal. The function of controller is to +measure the speed of fan and the edge number of input signal. The function +can be selected by devicetree setting. The edging mode and anti-jitter level +can also setted in devicetree. + +Required properties for tacho node: +- #address-cells : should be 1. + +- #size-cells : should be 1. + +- reg : address and length of the register set for the device. + +- compatible : should be "phytium,tacho" + +- clocks : phandle to clock provider with the clock number in the second cell + +Optional properties for tacho node: + +- tacho : set the controller work as fan tachometer, which is a default option. + +- capture : set the controller work as capture counter. + +------------------------------------------------------------------------------- + +- up : set the input edging mode as ascending, which is a default option. + +- down : set the input edging mode as descending. + +- double : set the input edging mode as doule-edging. + +------------------------------------------------------------------------------- + +- debounce-level : set the debounce-levle, which can be 0, 1, 2 and 3. + +Examples: + +tacho: tacho@28054000 { + #address-cells = <1>; + #size-cells = <1>; + reg = <0x0 0x28054000 0x0 0x1000>; + compatible = "phytium,tacho"; + clocks = <&sysclk>; + tacho; + up; + debounce-level = <2>; +}; diff --git a/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/i2c/phytium,i2c.yaml b/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/i2c/phytium,i2c.yaml new file mode 100755 index 000000000..62f496d0c --- /dev/null +++ b/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/i2c/phytium,i2c.yaml @@ -0,0 +1,48 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/i2c/snps,designware-i2c.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Phytium I2C/SMBus Controller + +maintainers: + - Chen Baozi + +allOf: + - $ref: /schemas/i2c/i2c-controller.yaml# + +properties: + compatible: + const: phytium,i2c + + reg: + minItems: 1 + items: + - description: Offset and length of the memory mapped registers + + interrupts: + maxItems: 1 + + clocks: + minItems: 1 + items: + - description: I2C controller reference clock source + +unevaluatedProperties: false + +required: + - compatible + - reg + - interrupts + +examples: + - | + i2c0: i2c@28011000 { + compatible = "phytium,i2c"; + reg = <0x0 0x28011000 0x0 0x1000>; + interrupts = ; + interrupt-names = "smbus_alert"; + clocks = <&sysclk_48mhz>; + }; +... diff --git a/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/iio/adc/phytium-adc.txt b/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/iio/adc/phytium-adc.txt new file mode 100755 index 000000000..edf0823d7 --- /dev/null +++ b/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/iio/adc/phytium-adc.txt @@ -0,0 +1,57 @@ +Phytium ADC + +This device is a 10-bit converter for 8 voltage channels. All inputs are +single ended. + +Required properties: +- compatible: Should be "phytium,adc" +- reg: memory window mapping address and length +- interrupts: Interrupt for the ADC control interface. +- clocks: Input clock used to derive the sample clock. +- #address-cells: Should be <1> (settings for the subnodes). +- #size-cells: Should be <0> (settings for the subnodes). + +Required subnodes: + +The ADC channels are configured as subnodes of the ADC. + +Required channel node properties: + +- reg: should contain the hardware channel number. + +Examples: + + adc0: adc@2807b000 { + compatible = "phytium,adc"; + reg = <0x0 0x2807b000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_48mhz>; + + #address-cells = <1>; + #size-cells = <0>; + + channel@0 { + reg = <0>; + }; + channel@1 { + reg = <1>; + }; + channel@2 { + reg = <2>; + }; + channel@3 { + reg = <3>; + }; + channel@4 { + reg = <4>; + }; + channel@5 { + reg = <5>; + }; + channel@6 { + reg = <6>; + }; + channel@7 { + reg = <7>; + }; + }; diff --git a/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/input/phytium-keypad.txt b/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/input/phytium-keypad.txt new file mode 100755 index 000000000..b34e6a1b3 --- /dev/null +++ b/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/input/phytium-keypad.txt @@ -0,0 +1,41 @@ +* Phytium Keypad Port device tree bindings + +The keypad port is designed to interface with a keypad matrix, which +simplify the software task of scanning a keypad matrix. It is capable +of detecting, debouncing, and decoding one or multiple keys pressed +simultaneously on a keypad. + +Required SoC Specific Properties: +- compatible: Should be "phytium,keypad". +- reg: Physical base address and length of memory mapped region. +- interrupts: Interrupt number to the CPU(s). + +Required Board Specific Properties: +- linux,keymap: The definition can be found at +bindings/input/matrix-keymap.txt. + +Example: + +keypad: keypad@2807a000 { + compatible = "phytium,keypad"; + reg = <0x 0x2807a000 0x0 0x1000>; + interrupts = ; + keypad,num-rows = <4>; + keypad,num-columns = <4>; + linux,keymap = <0x00000067 /* KEY_UP */ + 0x0001006c /* KEY_DOWN */ + 0x00020072 /* KEY_VOLUMEDOWN */ + 0x00030066 /* KEY_HOME */ + 0x0100006a /* KEY_RIGHT */ + 0x01010069 /* KEY_LEFT */ + 0x0102001c /* KEY_ENTER */ + 0x01030073 /* KEY_VOLUMEUP */ + 0x02000040 /* KEY_F6 */ + 0x02010042 /* KEY_F8 */ + 0x02020043 /* KEY_F9 */ + 0x02030044 /* KEY_F10 */ + 0x0300003b /* KEY_F1 */ + 0x0301003c /* KEY_F2 */ + 0x0302003d /* KEY_F3 */ + 0x03030074>; /* KEY_POWER */ +}; diff --git a/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/interrupt-controller/phytium,ixic.txt b/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/interrupt-controller/phytium,ixic.txt new file mode 100644 index 000000000..82180b38b --- /dev/null +++ b/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/interrupt-controller/phytium,ixic.txt @@ -0,0 +1,28 @@ +Phytium INTx interrupt controller (IXIC) + +This is a pseudo interrupt controller to handle PCI legacy interrupt on +some Phytium SoCs, which sits between the PCI INTx devices and the GIC +and forwards the 4 INTx input signals to 4 adjacent GICv3 SPIs. + +Required properties: + +- compatible : "phytium,ixic" +- reg : Specifies two regions of the register set, which + are called 'ctr' and 'hpb'. +- interrupt-controller : Identifies the node as an interrupt controller. +- #interrupt-cells : Specifies the number of cells needed to encode an + interrupt source. The value must be 3. +- intx-spi-base : The SPI number of the first SPI of the 4 adjacent + ones the IXIC forwards its interrupts to. + +Example: + ixic: interrupt-controller@29000000 { + compatible = "phytium,ixic"; + reg-names = "ctr", "hpb"; + reg = <0x0 0x29000000 0x0 0x00060000>, + <0x0 0x29100000 0x0 0x00002000>; + interrupt-controller; + interrupt-parent = <&gic>; + #interrupt-cells = <3>; + intx-spi-base = <28>; + }; diff --git a/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/interrupt-controller/phytium,ixic.yaml b/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/interrupt-controller/phytium,ixic.yaml new file mode 100755 index 000000000..67b17652e --- /dev/null +++ b/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/interrupt-controller/phytium,ixic.yaml @@ -0,0 +1,66 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/interrupt-controller/phytium,ixic.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Phytium INTx interrupt controller (IXIC) + +description: | +This is a psuedo interrupt controller to handle PCI legacy interrupt on +Phytium D2000 and FT-2000/4C SoC, which sits between the PCI INTx devices +and the GIC and forwards the 4 INTx input signals to 4 adjacent GICv3 SPIs. + +maintainers: + - Chen Baozi + +allOf: + - $ref: /schemas/interrupt-controller.yaml# + +properties: + compatible: + enum: + - phytium,d2000-ixic + - phytium,ft2004c-ixic + + reg: + description: | + Specifies two regions of the register set, which are called + 'ctr' and 'hpb' + minItems: 2 + maxItems: 2 + + interrupt-controller: true + + '#interrupt-cells': + description: | + Specifies the number of cells needed to encode an interrupt source. + const: 3 + + intx-spi-base: + $ref: /schemas/types.yaml#/definitions/uint32 + description: | + The SPI number of the first SPI of the 4 adjacent ones the IXIC + forwards its interrupts to. + +required: + - compatible + - reg + - interrupt-controller + - '#interrupt-cells' + - intx-spi-base + +additionalProperties: false + +examples: + - | + ixic: interrupt-controller@29000000 { + compatible = "phytium,d2000-ixic"; + reg-names = "ctr", "hpb"; + reg = <0x0 0x29000000 0x0 0x00060000>, + <0x0 0x29100000 0x0 0x00002000>; + interrupt-controller; + interrupt-parent = <&gic>; + #interrupt-cells = <3>; + intx-spi-base = <28>; + }; diff --git a/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/mailbox/phytium,mbox.yaml b/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/mailbox/phytium,mbox.yaml new file mode 100755 index 000000000..1061d26f2 --- /dev/null +++ b/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/mailbox/phytium,mbox.yaml @@ -0,0 +1,51 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mailbox/arm,mhuv2.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Phytium Mailbox Controller + +maintainers: + - Chen Baozi + +description: | + The Phytium mailbox controller that has a channel/link to communicate + with the remote end. A link raises interrupt for any received data. However, + there is no specified way of knowing if the sent data has been read by the + remote. This driver assumes the sender polls STAT register and the remote + clears it after having read the data. + +properties: + compatible: + - enum: phytium,mbox + + reg: + maxItems: 1 + + interrupts: + description: | + Contains the interrupt information corresponding to the link + maxItems: 1 + + '#mbox-cells': + description: | + It should be 1, which is the index of the channel needed. + const: 1 + +required: + - compatible + - reg + - interrupts + - '#mbox-cells' + +additionalProperties: false + +examples: + - | + mbox: mailbox@2a000000 { + compatible = "phytium,mbox"; + reg = <0x0 0x2a000000 0x0 0x1000>; + #mbox-cells = <1>; + interrupts = <0 48 4>; + }; diff --git a/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/media/phytium-jpeg.txt b/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/media/phytium-jpeg.txt new file mode 100755 index 000000000..c79e47f30 --- /dev/null +++ b/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/media/phytium-jpeg.txt @@ -0,0 +1,21 @@ +* Device tree bindings for Phytium JPEG Engine + +The JPEG Engine embedded in the Phytium SOCs can capture +and compress video data from digital or analog sources. + +Required properties: + - compatible: "phytium,jpeg" + - reg: contains the offset and length of the + JPEG Engine memory region + - interrupts: the interrupt associated with the VE on this platform + - phytium,ocm-buf-addr the physical address used to storage the inputing video data + +Example: + +jpeg: jpeg@32b32000 { + compatible = "phytium,jpeg"; + reg = <0x0 0x32b32000 0 0x1000>; + interrupts = <0 41 4>; + phytium,ocm-buf-addr = <0x30c40000 0x30c60000>; + status = "okay"; +}; diff --git a/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/mmc/phytium,sdci.yaml b/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/mmc/phytium,sdci.yaml new file mode 100755 index 000000000..1364583c9 --- /dev/null +++ b/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/mmc/phytium,sdci.yaml @@ -0,0 +1,58 @@ +# SPDX-License-Identifier: GPL-2.0-only +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mmc/phytium,sdci.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Phytium SDCI Controller Binding + +maintainers: + - Chen Baozi + +allOf: + - $ref: mmc-controller.yaml# + +properties: + compatible: + enum: + - phytium,sdci + + reg: + maxItems: 1 + + interrupts: + minItems: 3 + maxItems: 3 + + clocks: + minItems: 1 + items: + - description: core clock + + clock-names: + minItems: 1 + items: + - const: phytium_sdc_clk + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + +unevaluatedProperties: false + +examples: + - | + sdci: sdci@28207c00 { + compatible = "phytium,sdci"; + reg = <0x0 0x28207c00 0x0 0x100>; + interrupts = , + , + ; + clocks = <&sysclk_600mhz>; + clock-names = "phytium_sdc_clk"; + }; + +... diff --git a/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/mmc/phytium-mci.txt b/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/mmc/phytium-mci.txt new file mode 100755 index 000000000..129efb1fb --- /dev/null +++ b/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/mmc/phytium-mci.txt @@ -0,0 +1,36 @@ +* Phytium Multimedia Card Interface controller + +The highspeed MMC host controller on Phytium SoCs provides an interface +for MMC, SD and SDIO types of memory cards. + +Required properties: +- compatible : should be "phytium,mci". +- reg: mmc controller base registers. +- clocks : phandles to input clocks. +- clock-names : should be "phytium_mci_clk". +- interrupts : mmc controller interrupt. + +Examples: + - Within .dtsi: + mmc0: mmc@28000000 { + compatible = "phytium,mci"; + reg = <0x0 0x28000000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_1200mhz>; + clock-names = "phytium_mci_clk"; + status = "disabled"; + }; + + - Within dts: + &mmc0 { + bus-width = <4>; + max-frequency = <50000000>; + cap-sdio-irq; + cap-sd-highspeed; + sd-uhs-sdr12; + sd-uhs-sdr25; + sd-uhs-sdr50; + no-mmc; + status = "ok"; + }; + diff --git a/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/net/can/phytium-can.txt b/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/net/can/phytium-can.txt new file mode 100755 index 000000000..ab109e540 --- /dev/null +++ b/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/net/can/phytium-can.txt @@ -0,0 +1,29 @@ +Phytium CAN controller +------------------------ + +Required properties: +- compatible: Should be: + - "phytium,can" for Phytium CAN controllers + - "phytium,canfd" for Phytium CAN controllers with CANFD support +- reg: Should contain CANFD controller registers location and length +- interrupts: Should contain IRQ line for the CANFD controller +- clocks: CLocks used by the controller +- clock-names: Input clock names, should be "can_clk" +- tx-fifo-depth: Indicates the length of TX FIFO +- rx-fifo-depth: Indicates the length of TX FIFO + +Optional property: +- extend_brp: Indicates to apply the extend BRP parameter of bit timming for + early version of CAN controller + +Example: + + can0: can@2800a000{ + compatible = "phytium,canfd"; + reg = <0x0 0x2800a000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_600mhz>; + clock-names = "can_clk"; + tx-fifo-depth = <64>; + rx-fifo-depth = <64>; + }; diff --git a/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/net/phytium,gmac.yaml b/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/net/phytium,gmac.yaml new file mode 100755 index 000000000..486f10a80 --- /dev/null +++ b/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/net/phytium,gmac.yaml @@ -0,0 +1,54 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/net/phytium,gmac.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Phytium 10/100/1000 Ethernet driver (GMAC) + +maintainers: + - Chen Baozi + +# We need a select here so we don't match all nodes with 'snps,dwmac' +select: + properties: + compatible: + contains: + enum: + - phytium,gmac + required: + - compatible + +allOf: + - $ref: "snps,dwmac.yaml#" + +properties: + compatible: + - items: + - enum: + - phytium,gmac + + clock-frequency: true + +required: + - compatible + - clock-frequency + +unevaluatedProperties: false + +examples: + - | + gmac0: eth@2820c000 { + compatible = "phytium,gmac"; + reg = <0x0 0x2820c000 0x0 0x2000>; + interrupts = ; + clock-frequency = <250000000>; + + snps,pbl = <16>; + snps,fixed-burst; + snps,multicast-filter-bins = <64>; + snps,perfect-filter-entries = <128>; + tx-fifo-depth = <4096>; + rx-fifo-depth = <4096>; + max-frame-size = <9000>; + }; diff --git a/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/pwm/pwm-phytium.txt b/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/pwm/pwm-phytium.txt new file mode 100755 index 000000000..0285e95a3 --- /dev/null +++ b/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/pwm/pwm-phytium.txt @@ -0,0 +1,23 @@ +Phytium PWM controller + +Required properties: +- compatible: should be "phytium,pwm" +- reg: physical base address and length of the controller's registers +- interrupts: interrupt for the pwm controller +- clocks : clock specifiers for both ipg and per clocks. +- phytium,db: One or two to describe dead-band configurations. + "cntmod" indicates the counter mode (0 for modulo, 1 for up-and-down). + "dutymod" indicdates which duty to compare with (0 for PMW_CCR, 1 for FIFO). + "div" selects the clock divider value, from 0 to 1023. + "updbcly" selects the rising edge delay cycles. + "dbpolarity" selects the polarity for dead-band. + +Example: + +pwm0: pwm@2804a000 { + compatible = "phytium,pwm"; + reg= <0x0 0x2804a000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_48mhz>; + phytium,db = <0 0 0 1000 0>; +}; diff --git a/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/remoteproc/homo-rproc.txt b/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/remoteproc/homo-rproc.txt new file mode 100644 index 000000000..00f4c4402 --- /dev/null +++ b/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/remoteproc/homo-rproc.txt @@ -0,0 +1,39 @@ +homogeneous remoteproc driver +========================================== + +This driver implements communication between remote processors under a homogeneous CPU architecture(SMP). + +Homogeneous RemoteProc Device Node: +================================= +A homo_remoteproc device node is used to represent the remote core instance within SoC. + +Required properties: +-------------------- + - compatible : should be "homo,rproc" + - remote-processor: remote processor's linux cpu logical number + - inter-processor-interrupt: IPI/SGI interrupt number, default is 9 + - memory-region: reserved memory which will be used by remote processor + - firmware-name: the name of openamp image, default is "openamp_core0.elf" + +Example: +-------- + + reserved-memory { + #address-cells = <0x2>; + #size-cells = <0x2>; + ranges; + + rproc: rproc@b0100000 { + no-map; + reg = <0x0 0xb0100000 0x0 0x19900000>; + }; + }; + + homo_rproc: homo_rproc@0 { + compatible = "homo,rproc"; + remote-processor = <3>; + inter-processor-interrupt = <9>; + memory-region = <&rproc>; + firmware-name = "openamp_core0.elf"; + status = "disabled"; + }; diff --git a/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/rng/phytium-rng.txt b/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/rng/phytium-rng.txt new file mode 100755 index 000000000..ee893f65c --- /dev/null +++ b/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/rng/phytium-rng.txt @@ -0,0 +1,13 @@ +Phytium Random Number Generator + +Required properties: +- compatible : Should be "phytium,rng" +- reg : Offset and length of the register set of this block + +Example: + + rng@0x31a06000 { + compatible = "phytium,rng"; + reg = <0x0 0x31a06000 0x0 0x1000>; + }; + diff --git a/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/spi/spi-phytium.txt b/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/spi/spi-phytium.txt new file mode 100755 index 000000000..a674d1921 --- /dev/null +++ b/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/spi/spi-phytium.txt @@ -0,0 +1,24 @@ +Phytium SPI controller + +Required properties: +- compatible: should be "phytium,spi" +- #address-cells: see spi-bus.txt +- #size-cells: see spi-bus.txt +- reg: address and length of the spi master registers +- interrupts: should contain one interrupt +- clocks: spi clock phandle +- num-cs: see spi-bus.txt + +Optional properties: +- cs-gpios: see spi-bus.txt + +Example: + + +spi0: spi@2800c000 { + compatible = "phytium,spi"; + interrupts = ; + reg = <0x0 0x2800c000 0x0 0x1000>; + clocks = <&sysclk_48mhz>; + num-cs = <4>; +}; diff --git a/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/w1/phytium-w1.txt b/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/w1/phytium-w1.txt new file mode 100755 index 000000000..43784c3aa --- /dev/null +++ b/target/linux/phytium/files-5.10/Documentation/devicetree/bindings/w1/phytium-w1.txt @@ -0,0 +1,14 @@ +* Phytium 1-wire bus master controller + +Required properties: +- compatible : should be "phytium,w1" +- reg : Address and length of the register set for the device +- interrupts : interrupt line. + +Example: + + onewire0: onewire@2803f000 { + compatible = "phytium,w1"; + reg = <0x0 0x2803f000 0x0 0x1000>; + interrupts = ; + }; diff --git a/target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/Makefile b/target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/Makefile new file mode 100644 index 000000000..037b33ba9 --- /dev/null +++ b/target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/Makefile @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: GPL-2.0 + +## pe220x general demo development board series. +dtb-$(CONFIG_ARCH_PHYTIUM) += e2000q-demo-board.dtb +dtb-$(CONFIG_ARCH_PHYTIUM) += e2000d-demo-board.dtb +dtb-$(CONFIG_ARCH_PHYTIUM) += e2000s-demo-board.dtb + +## pe220x specific industry development board series. +dtb-$(CONFIG_ARCH_PHYTIUM) += e2000q-miniitx-board.dtb +dtb-$(CONFIG_ARCH_PHYTIUM) += e2000d-miniitx-board.dtb +dtb-$(CONFIG_ARCH_PHYTIUM) += e2000q-vpx-board.dtb +dtb-$(CONFIG_ARCH_PHYTIUM) += e2000q-edu-board.dtb +dtb-$(CONFIG_ARCH_PHYTIUM) += e2000q-come-board.dtb +dtb-$(CONFIG_ARCH_PHYTIUM) += e2000d-power-board.dtb +dtb-$(CONFIG_ARCH_PHYTIUM) += e2000q-hanwei-board.dtb +dtb-$(CONFIG_ARCH_PHYTIUM) += phytiumpi_firefly.dtb +dtb-$(CONFIG_ARCH_PHYTIUM) += e2000d-chillipi-edu-board.dtb diff --git a/target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/e2000d-chillipi-edu-board.dts b/target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/e2000d-chillipi-edu-board.dts new file mode 100644 index 000000000..28a1f0d47 --- /dev/null +++ b/target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/e2000d-chillipi-edu-board.dts @@ -0,0 +1,339 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * DTS file for Phytium Pe2202 chillipi education board + * + * Copyright (c) 2022-2023 Phytium Technology Co., Ltd. + * + * 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. + */ + +/dts-v1/; +/memreserve/ 0x80000000 0x10000; + +#include "pe2202.dtsi" +#include "dt-bindings/gpio/gpio.h" + + +/{ + model = "Pe2202 CHILLIPI EDUCATION BOARD"; + compatible = "phytium,pe2202"; + + chosen { + stdout-path = "serial1:115200n8"; + }; + + memory@00{ + device_type = "memory"; + reg = <0x0 0x80000000 0x0 0x80000000>; + }; + + sound_card: sound { + compatible = "simple-audio-card"; + simple-audio-card,format = "i2s"; + simple-audio-card,name = "phytium,pe220x-i2s-audio"; + simple-audio-card,pin-switches = "mic-in"; + simple-audio-card,widgets = "Microphone","mic-in"; + simple-audio-card,routing = "MIC2","mic-in"; + simple-audio-card,cpu { + sound-dai = <&i2s0>; + }; + simple-audio-card,codec{ + sound-dai = <&codec0>; + }; + }; +}; + +&soc { + mio6: i2c@28020000 { + compatible = "phytium,i2c"; + reg = <0x0 0x28020000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + oled@3c { + compatible = "SinoWealth,sh1106"; + reg = <0x3c>; + }; + + pcf8591@48 { + compatible = "philips,pcf8591"; + reg = <0x48>; + }; + }; + + mio9: i2c@28026000 { + compatible = "phytium,i2c"; + reg = <0x0 0x28026000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + + /* localized chillipi education board use this rtc device */ + rtc@32 { + compatible = "wave,sd3068"; + reg = <0x32>; + status = "disabled"; + }; + + rtc@68 { + compatible = "dallas,ds1339"; + reg = <0x68>; + status = "disabled"; + }; + }; + + mio14: i2c@28030000 { + compatible = "phytium,i2c"; + reg = <0x0 0x28030000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + + codec0: es8336@10 { + det-gpios = <&gpio2 5 GPIO_ACTIVE_LOW>; + sel-gpios = <&gpio2 11 GPIO_ACTIVE_LOW>; + #sound-dai-cells = <0>; + compatible = "everest,es8336"; + reg = <0x10>; + status = "disabled"; + }; + }; +}; + +&gpio0 { + status = "okay"; +}; + +&gpio1 { + status = "okay"; +}; + +&gpio2 { + status = "okay"; +}; + +&gpio3 { + status = "okay"; +}; + +&gpio4 { + interrupt-controller; + #interrupt-cells = <2>; + status = "okay"; +}; + +&gpio5 { + status = "okay"; +}; + +&watchdog0 { + status = "okay"; +}; + +&watchdog1 { + status = "okay"; +}; + +&pcie { + status = "okay"; +}; + +&usb3_0 { + status = "okay"; +}; + +&usb3_1 { + status = "okay"; +}; + +&usb2_0 { + dr_mode = "host"; + status = "okay"; +}; + +&usb2_1 { + dr_mode = "host"; + status = "disabled"; +}; + +&usb2_2 { + dr_mode = "host"; + status = "disabled"; +}; + +&usb2_3 { + dr_mode = "host"; + status = "okay"; +}; + +&usb2_4 { + dr_mode = "host"; + status = "okay"; +}; + +&macb0 { + phy-mode = "sgmii"; + use-mii; + status = "okay"; +}; + +/* + * In chillipi education board, M.2 interface may insert two types of disks: PCIE (nvme protocol) or SATA (ahci protocol), + * so here enable 'sata0' node by default to correspond to the sata drive case. Pin multiplexing exists + * in PCIE2 and SATA0, when used as a boot disk, the BIOS can automatically recognize these two conditions. + */ +&sata0 { + status = "disabled"; +}; + +&sata1 { + status = "disabled"; +}; + +&qspi0 { + status = "okay"; + + flash@0 { + status = "okay"; + }; +}; + +&spi0 { + global-cs = <1>; + status = "okay"; + + ILI9488@0 { + compatible = "ilitek,ili9488"; + reg = <0>; + spi-max-frequency = <50000000>; + + rotate = <0>; + fps = <30>; + buswidth = <8>; + regwidth = <8>; + bgr; + reset-gpios = <&gpio0 15 GPIO_ACTIVE_LOW>; + dc-gpios = <&gpio3 2 GPIO_ACTIVE_LOW>; + //cs-gpios = <&gpio3 2 GPIO_ACTIVE_LOW>; + + //backlight = <&backlight>; + debug = <0>; + }; + + ADS7843@1 { + compatible = "ti,ads7843"; + spi-max-frequency = <1000000>; + reg = <1>; + //cs-gpios = <&gpio4 10 GPIO_ACTIVE_LOW>; + interrupt-parent = <&gpio4>; + interrupts = <12 0>; + pendown-gpio = <&gpio4 12 0>; + + ti,x-min = /bits/ 16 <0>; + //ti,x-max = /bits/ 16 <8000>; + ti,x-max = /bits/ 16 <3200>; + ti,y-min = /bits/ 16 <0>; + //ti,y-max = /bits/ 16 <4800>; + ti,y-max = /bits/ 16 <4800>; + ti,x-plate-ohms = /bits/ 16 <40>; + ti,pressure-max = /bits/ 16 <255>; + + wakeup-source; + }; +}; + +&spi2 { + global-cs = <1>; + status = "okay"; + + mpu6500@0 { + compatible = "InvenSense,mpu6500"; + spi-max-frequency = <500000>; + reg = <0>; + spi-cpol; + spi-cpha; + }; +}; + +&uart1 { + status = "okay"; +}; + +&uart2 { + status = "okay"; +}; + +&can0 { + status = "okay"; +}; + +&can1 { + status = "okay"; +}; + +&mmc0 { + bus-width = <0x00000008>; + max-frequency = <100000000>; + cap-mmc-hw-reset; + cap-mmc-highspeed; + mmc-hs200-1_8v; + no-sdio; + no-sd; + non-removable; + status = "okay"; +}; + +&mmc1 { + bus-width = <0x00000004>; + max-frequency = <50000000>; + cap-sdio-irq; + cap-sd-highspeed; + no-sdio; + no-mmc; + status = "okay"; +}; + +&i2s0 { + #sound-dai-cells = <0>; + status = "okay"; +}; + +&dc0 { + pipe_mask = /bits/ 8 <0x2>; + edp_mask = /bits/ 8 <0x0>; + status = "okay"; +}; + +&i2s_dp1 { + status = "okay"; +}; + +&pmdk_dp { + num-dp = <1>; + dp-mask = /bits/ 8 <0x2>; + status = "okay"; +}; + +&rng0 { + status = "okay"; +}; + +&pwm0 { + phytium,db = <0 0 0 1000 1000 0>; + status = "okay"; +}; + +&pwm1 { + phytium,db = <0 0 0 1000 1000 0>; + status = "okay"; +}; diff --git a/target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/e2000d-demo-board.dts b/target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/e2000d-demo-board.dts new file mode 100644 index 000000000..bdfe8f2fc --- /dev/null +++ b/target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/e2000d-demo-board.dts @@ -0,0 +1,242 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * DTS file for Phytium Pe2202 demo board + * + * Copyright (c) 2022-2023 Phytium Technology Co., Ltd. + * + * 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. + */ + +/dts-v1/; +/memreserve/ 0x80000000 0x10000; + +#include "pe2202.dtsi" +#include "dt-bindings/gpio/gpio.h" + + +/{ + model = "Pe2202 DEMO DDR4"; + compatible = "phytium,pe2202"; + + chosen { + stdout-path = "serial1:115200n8"; + }; + + memory@00{ + device_type = "memory"; + reg = <0x0 0x80000000 0x0 0x80000000>; + }; + + sound_card: sound { + compatible = "simple-audio-card"; + simple-audio-card,format = "i2s"; + simple-audio-card,name = "phytium,pe220x-i2s-audio"; + simple-audio-card,pin-switches = "mic-in"; + simple-audio-card,widgets = "Microphone","mic-in"; + simple-audio-card,routing = "MIC2","mic-in"; + simple-audio-card,cpu { + sound-dai = <&i2s0>; + }; + simple-audio-card,codec{ + sound-dai = <&codec0>; + }; + }; +}; + +&soc { + mio9: i2c@28026000 { + compatible = "phytium,i2c"; + reg = <0x0 0x28026000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + /* localized demo board use this rtc device */ + rtc@32 { + compatible = "wave,sd3068"; + reg = <0x32>; + }; + + rtc@68 { + compatible = "dallas,ds1339"; + reg = <0x68>; + }; + }; + + mio14: i2c@28030000 { + compatible = "phytium,i2c"; + reg = <0x0 0x28030000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + codec0: es8336@10 { + det-gpios = <&gpio2 5 GPIO_ACTIVE_LOW>; + sel-gpios = <&gpio2 11 GPIO_ACTIVE_LOW>; + #sound-dai-cells = <0>; + compatible = "everest,es8336"; + reg = <0x10>; + }; + }; +}; + +&gpio0 { + status = "okay"; +}; + +&gpio1 { + status = "okay"; +}; + +&gpio2 { + status = "okay"; +}; + +&gpio3 { + status = "okay"; +}; + +&gpio4 { + status = "okay"; +}; + +&gpio5 { + status = "okay"; +}; + +&watchdog0 { + status = "okay"; +}; + +&watchdog1 { + status = "okay"; +}; + +&pcie { + status = "okay"; +}; + +&usb3_0 { + status = "okay"; +}; + +&usb3_1 { + status = "okay"; +}; + +&usb2_0 { + dr_mode = "otg"; + status = "okay"; +}; + +&usb2_1 { + dr_mode = "peripheral"; + status = "okay"; +}; + +&usb2_2 { + dr_mode = "peripheral"; + status = "okay"; +}; + +&macb0 { + phy-mode = "sgmii"; + use-mii; + status = "okay"; +}; + +/* + * In demo board, M.2 interface may insert two types of disks: PCIE (nvme protocol) or SATA (ahci protocol), + * so here enable 'sata0' node by default to correspond to the sata drive case. Pin multiplexing exists + * in PCIE2 and SATA0, when used as a boot disk, the BIOS can automatically recognize these two conditions. + */ +&sata0 { + status = "okay"; +}; + +&sata1 { + status = "okay"; +}; + +&qspi0 { + status = "okay"; + + flash@0 { + status = "okay"; + }; +}; + +&spi2 { + global-cs = <1>; + status = "okay"; +}; + +&uart1 { + status = "okay"; +}; + +&uart2 { + status = "okay"; +}; + +&can0 { + status = "okay"; +}; + +&can1 { + status = "okay"; +}; + +&mmc0 { + bus-width = <0x00000008>; + max-frequency = <100000000>; + cap-mmc-hw-reset; + cap-mmc-highspeed; + mmc-hs200-1_8v; + no-sdio; + no-sd; + non-removable; + status = "okay"; +}; + +&mmc1 { + bus-width = <0x00000004>; + max-frequency = <50000000>; + cap-sdio-irq; + cap-sd-highspeed; + no-sdio; + no-mmc; + status = "okay"; +}; + +&i2s0 { + #sound-dai-cells = <0>; + status = "okay"; +}; + +&dc0 { + pipe_mask = /bits/ 8 <0x2>; + edp_mask = /bits/ 8 <0x0>; + status = "okay"; +}; + +&i2s_dp1 { + status = "okay"; +}; + +&pmdk_dp { + num-dp = <1>; + dp-mask = /bits/ 8 <0x2>; + status = "okay"; +}; + +&rng0 { + status = "okay"; +}; diff --git a/target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/e2000d-miniitx-board.dts b/target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/e2000d-miniitx-board.dts new file mode 100644 index 000000000..631c967c1 --- /dev/null +++ b/target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/e2000d-miniitx-board.dts @@ -0,0 +1,296 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * DTS file for Phytium Pe2202 miniitx board + * + * Copyright (c) 2022-2023 Phytium Technology Co., Ltd. + * + * Hongmin Qi + * + * 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. + */ + +/dts-v1/; +/memreserve/ 0x80000000 0x10000; + +#include "pe2202.dtsi" + +/{ + model = "Pe2202 MINIITX Board"; + compatible = "phytium,pe2202"; + + chosen { + stdout-path = "serial1:115200n8"; + }; + + memory@00{ + device_type = "memory"; + reg = <0x0 0x80000000 0x1 0x00000000>; + }; + + sound_card: sound { + compatible = "simple-audio-card"; + simple-audio-card,format = "i2s"; + simple-audio-card,name = "phytium,pe220x-i2s-audio"; + simple-audio-card,cpu { + sound-dai = <&i2s0>; + }; + simple-audio-card,codec { + sound-dai = <&codec0>; + }; + }; +}; + +&soc { + mio0: uart@28014000 { + compatible = "arm,pl011","arm,primecell"; + reg = <0x0 0x28014000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz &sysclk_50mhz>; + clock-names = "uartclk", "apb_pclk"; + status = "disabled"; + }; + + mio1: uart@28016000 { + compatible = "arm,pl011","arm,primecell"; + reg = <0x0 0x28016000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz &sysclk_50mhz>; + clock-names = "uartclk", "apb_pclk"; + status = "disabled"; + }; + + mio9: i2c@28026000 { + compatible = "phytium,i2c"; + reg = <0x0 0x28026000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + rtc@68 { + compatible = "dallas,ds1339"; + reg = <0x68>; + }; + }; + + mio6: uart@28020000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0x0 0x28020000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz &sysclk_50mhz>; + clock-names = "uartclk", "apb_pclk"; + status = "okay"; + }; + + mio7: uart@28022000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0x0 0x28022000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz &sysclk_50mhz>; + clock-names = "uartclk", "apb_pclk"; + status = "okay"; + }; + + mio14: i2c@28030000 { + compatible = "phytium,i2c"; + reg = <0x0 0x28030000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + codec0: es8336@10 { + det-gpios = <&gpio2 5 0>; + sel-gpios = <&gpio2 6 0>; + #sound-dai-cells = <0>; + compatible = "everest,es8336"; + reg = <0x10>; + mic-src = [20]; + }; + }; + + mio15: uart@28032000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0x0 0x28032000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz &sysclk_50mhz>; + clock-names = "uartclk", "apb_pclk"; + status = "okay"; + }; +}; + +&pcie { + status = "okay"; +}; + +&usb3_0 { + status = "okay"; +}; + +&usb3_1 { + status = "okay"; +}; + +&usb2_0 { + dr_mode = "otg"; + status = "okay"; +}; + +&usb2_1 { + dr_mode = "host"; + status = "disabled"; +}; + +&usb2_2 { + dr_mode = "host"; + status = "disabled"; +}; + +&usb2_3 { + dr_mode = "host"; + status = "okay"; +}; + +&usb2_4 { + dr_mode = "host"; + status = "okay"; +}; + +&macb0 { + phy-mode = "sgmii"; + use-mii; + status = "okay"; +}; + +&macb1 { + phy-mode = "sgmii"; + use-mii; + status = "disabled"; +}; + +&macb2 { + phy-mode = "sgmii"; + use-mii; + status = "okay"; +}; + +&macb3 { + phy-mode = "sgmii"; + use-mii; + status = "disabled"; +}; + +&can0 { + status = "okay"; +}; + +&can1 { + status = "okay"; +}; + +&qspi0 { + status = "okay"; + + flash@0 { + status = "okay"; + }; +}; + +&spi0 { + global-cs = <1>; + status = "disabled"; + + flash: w25q128@0 { + compatible = "winbond,w25q128", "jedec,spi-nor"; + spi-tx-bus-width = <1>; + spi-rx-bus-width = <1>; + spi-max-frequency = <12000000>; + reg = <0x00>; + status = "disabled"; + }; +}; + +&spi2 { + global-cs = <1>; + status = "disabled"; +}; + +&spi3 { + global-cs = <1>; + status = "disabled"; +}; + +&mmc0 { + status = "okay"; + bus-width = <0x4>; + max-frequency = <50000000>; + cap-sdio-irq; + cap-sd-highspeed; + no-mmc; +}; + +&mmc1 { + status = "okay"; +}; + +&uart0 { + status = "okay"; +}; + +&uart1 { + status = "okay"; +}; + +&uart2 { + status = "okay"; +}; + +&uart3 { + status = "okay"; +}; + +&gpio1 { + status = "okay"; +}; + +&gpio2 { + status = "okay"; +}; + +&gpio3 { + status = "okay"; +}; + +&gpio4 { + status = "okay"; +}; + +&sata0 { + status = "okay"; +}; + +&sata1 { + status = "okay"; +}; + +&dc0 { + status = "okay"; + pipe_mask = [02]; + edp_mask = [00]; +}; + +&i2s0 { + #sound-dai-cells = <0>; + dai-name = "phytium-i2s-lsd"; + status = "okay"; +}; + +&pwm1 { + phytium,db = <0 0 0 1000 1000 0>; + status = "okay"; +}; diff --git a/target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/e2000d-power-board.dts b/target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/e2000d-power-board.dts new file mode 100755 index 000000000..8d481b8c1 --- /dev/null +++ b/target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/e2000d-power-board.dts @@ -0,0 +1,243 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * DTS file for Phytium Pe2202 power board + * + * Copyright (c) 2022-2023 Phytium Technology Co., Ltd. + * + * Hongmin Qi + * + * 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. + */ + +/dts-v1/; +/memreserve/ 0x80000000 0x10000; + +#include "pe2202.dtsi" + +/{ + model = "Pe2202S power Board"; + compatible = "phytium,pe2202"; + + chosen { + stdout-path = "serial1:115200n8"; + }; + + memory@00{ + device_type = "memory"; + reg = <0x0 0x80000000 0x1 0x00000000>; + }; +}; + +&soc { + mio9: i2c@28026000 { + compatible = "phytium,i2c"; + reg = <0x0 0x28026000 0x0 0x1000>; + interrupts = <0x0 0x65 0x4>; + clocks = <&sysclk_50mhz>; + #address-cells = <0x1>; + #size-cells = <0x0>; + status = "okay"; + + rtc@68 { + compatible = "dallas,ds1339"; + reg = <0x68>; + }; + }; + + mio6: i2c@28020000 { + status = "okay"; + compatible = "phytium,i2c"; + reg = <0x0 0x28020000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + #address-cells = <0x1>; + #size-cells = <0x0>; + eeprom@50 { + compatible = "atmel,24c02"; + reg = <0x50>; + pagesize = <1>; + }; + }; + + mio10: uart@28028000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0x0 0x28028000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz &sysclk_50mhz>; + clock-names = "uartclk", "apb_pclk"; + status = "okay"; + }; + + mio11: uart@2802a000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0x0 0x2802a000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz &sysclk_50mhz>; + clock-names = "uartclk", "apb_pclk"; + status = "okay"; + }; + + mio13: uart@2802e000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0x0 0x2802e000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz &sysclk_50mhz>; + clock-names = "uartclk", "apb_pclk"; + status = "okay"; + }; + + mio14: uart@28030000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0x0 0x28030000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz &sysclk_50mhz>; + clock-names = "uartclk", "apb_pclk"; + status = "okay"; + }; + + mio15: uart@28032000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0x0 0x28032000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz &sysclk_50mhz>; + clock-names = "uartclk", "apb_pclk"; + status = "okay"; + }; +}; + +&pcie { + status = "okay"; +}; + +&usb3_0 { + status = "okay"; +}; + +&usb3_1 { + status = "okay"; +}; + +&usb2_0 { + dr_mode = "otg"; + status = "okay"; +}; + +&usb2_3 { + dr_mode = "host"; + status = "okay"; +}; + +&usb2_4 { + dr_mode = "host"; + status = "okay"; +}; + +&macb0 { + phy-mode = "sgmii"; + use-mii; + status = "okay"; +}; + +&macb1 { + phy-mode = "sgmii"; + use-mii; + status = "okay"; +}; + +&macb2 { + phy-mode = "sgmii"; + use-mii; + status = "okay"; +}; + +&macb3 { + phy-mode = "sgmii"; + use-mii; + status = "disabled"; +}; + +&dc0 { + pipe_mask = [02]; + edp_mask = [00]; + status = "okay"; +}; + +&can0 { + status = "okay"; +}; + +&can1 { + status = "okay"; +}; + +&qspi0 { + status = "okay"; + + flash@0 { + status = "okay"; + }; +}; + +&spi0 { + global-cs = <1>; + status = "disabled"; +}; + +&mmc0 { + bus-width = <8>; + max-frequency = <50000000>; + cap-mmc-hw-reset; + cap-mmc-highspeed; + no-sdio; + no-sd; + non-removable; + status = "okay"; +}; + +&mmc1 { + status = "okay"; +}; + +&uart0 { + status = "okay"; +}; + +&uart1 { + status = "okay"; +}; + +&uart2 { + status = "okay"; +}; + +&uart3 { + status = "okay"; +}; + + +&gpio0 { + status = "okay"; +}; + +&gpio1 { + status = "okay"; +}; + +&gpio2 { + status = "okay"; +}; + +&gpio3 { + status = "okay"; +}; + +&gpio4 { + status = "okay"; +}; + +&gpio5 { + status = "okay"; +}; diff --git a/target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/e2000q-come-board.dts b/target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/e2000q-come-board.dts new file mode 100755 index 000000000..f98655dd4 --- /dev/null +++ b/target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/e2000q-come-board.dts @@ -0,0 +1,265 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * DTS file for Phytium Pe2204 come board + * + * Copyright (c) 2022-2023 Phytium Technology Co., Ltd. + * + * Hongmin Qi + * + * 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. + */ + +/dts-v1/; +/memreserve/ 0x80000000 0x10000; + +#include "pe2204.dtsi" + +/{ + model = "Pe2204 come Board"; + compatible = "phytium,pe2204"; + + chosen { + stdout-path = "serial1:115200n8"; + }; + + memory@00{ + device_type = "memory"; + reg = <0x0 0x80000000 0x2 0x00000000>; + }; +}; + +&soc { + mio3: i2c@2801a000 { + status = "okay"; + compatible = "phytium,i2c"; + reg = <0x0 0x2801a000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + #address-cells = <1>; + #size-cells = <0>; + + eeprom@50 { + compatible = "atmel,24c02"; + reg = <0x50>; + pagesize = <1>; + }; + }; + + mio9: i2c@28026000 { + compatible = "phytium,i2c"; + reg = <0x0 0x28026000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + rtc@32 { + compatible = "wave,sd3068"; + reg = <0x32>; + }; + }; + + mio6: uart@28020000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0x0 0x28020000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz &sysclk_50mhz>; + clock-names = "uartclk", "apb_pclk"; + status = "okay"; + }; + + mio10: uart@28028000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0x0 0x28028000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz &sysclk_50mhz>; + clock-names = "uartclk", "apb_pclk"; + status = "okay"; + }; + + mio14: uart@28030000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0x0 0x28030000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz &sysclk_50mhz>; + clock-names = "uartclk", "apb_pclk"; + status = "okay"; + }; + + mio15: uart@28032000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0x0 0x28032000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz &sysclk_50mhz>; + clock-names = "uartclk", "apb_pclk"; + status = "okay"; + }; +}; + +&pcie { + status = "okay"; +}; + +&usb3_0 { + status = "okay"; +}; + +&usb3_1 { + status = "okay"; +}; + +&usb2_0 { + dr_mode = "otg"; + status = "okay"; +}; + +&usb2_3 { + dr_mode = "host"; + status = "okay"; +}; + +&usb2_4 { + dr_mode = "host"; + status = "okay"; +}; + +&macb0 { + phy-mode = "sgmii"; + use-mii; + status = "okay"; +}; + +&macb1 { + phy-mode = "sgmii"; + use-mii; + status = "disabled"; +}; + +&macb2 { + phy-mode = "rgmii"; + use-mii; + status = "disabled"; +}; + +&macb3 { + phy-mode = "rgmii"; + use-mii; + status = "disabled"; +}; + +&can0 { + status = "okay"; +}; + +&can1 { + status = "okay"; +}; + +&qspi0 { + status = "okay"; + + flash@0 { + status = "okay"; + }; +}; + +&spi0 { + global-cs = <1>; + status = "disabled"; +}; + +&spi2 { + global-cs = <1>; + status = "okay"; + + flash: w25q128@0 { + compatible = "winbond,w25q128", "jedec,spi-nor"; + spi-tx-bus-width = <1>; + spi-rx-bus-width = <1>; + spi-max-frequency = <12000000>; + reg = <0x00>; + }; +}; + +&mmc0 { + bus-width = <4>; + max-frequency = <25000000>; + cap-mmc-hw-reset; + cap-mmc-highspeed; + no-sdio; + no-sd; + non-removable; + status = "okay"; +}; + +&mmc1 { + status = "okay"; +}; + +&uart0 { + status = "okay"; +}; + +&uart1 { + status = "okay"; +}; + +&uart2 { + status = "okay"; +}; + +&uart3 { + status = "okay"; +}; + +&sata0 { + status = "okay"; +}; + +&sata1 { + status = "okay"; +}; + +&hda0 { + status = "okay"; +}; + +&dc0 { + status = "okay"; + pipe_mask = [03]; + edp_mask = [00]; +}; + +&gpio0 { + status = "okay"; +}; + +&gpio1 { + status = "okay"; +}; + +&gpio2 { + status = "okay"; +}; + +&gpio3 { + status = "okay"; +}; + +&gpio4 { + status = "okay"; +}; + +&gpio5 { + status = "okay"; +}; + +&pwm1 { + phytium,db = <0 0 0 1000 1000 0>; + status = "okay"; +}; + diff --git a/target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/e2000q-demo-board.dts b/target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/e2000q-demo-board.dts new file mode 100644 index 000000000..33c6b9827 --- /dev/null +++ b/target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/e2000q-demo-board.dts @@ -0,0 +1,246 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * DTS file for Phytium Pe2204 demo board + * + * Copyright (c) 2022-2023 Phytium Technology Co., Ltd. + * + * 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. + */ + +/dts-v1/; +/memreserve/ 0x80000000 0x10000; + +#include "pe2204.dtsi" +#include "dt-bindings/gpio/gpio.h" + +/{ + model = "Pe2204 DEMO DDR4"; + compatible = "phytium,pe2204"; + + chosen { + stdout-path = "serial1:115200n8"; + }; + + memory@00{ + device_type = "memory"; + reg = <0x0 0x80000000 0x0 0x80000000>; + }; + + sound_card: sound { + compatible = "simple-audio-card"; + simple-audio-card,format = "i2s"; + simple-audio-card,name = "phytium,pe220x-i2s-audio"; + simple-audio-card,pin-switches = "mic-in"; + simple-audio-card,widgets = "Microphone","mic-in"; + simple-audio-card,routing = "MIC2","mic-in"; + simple-audio-card,cpu { + sound-dai = <&i2s0>; + }; + simple-audio-card,codec{ + sound-dai = <&codec0>; + }; + }; +}; + +&soc { + mio9: i2c@28026000 { + compatible = "phytium,i2c"; + reg = <0x0 0x28026000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + /* localized demo board use this rtc device */ + rtc@32 { + compatible = "wave,sd3068"; + reg = <0x32>; + }; + + rtc@68 { + compatible = "dallas,ds1339"; + reg = <0x68>; + }; + }; + + mio14: i2c@28030000 { + compatible = "phytium,i2c"; + reg = <0x0 0x28030000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + codec0: es8336@10 { + det-gpios = <&gpio2 5 GPIO_ACTIVE_LOW>; + sel-gpios = <&gpio2 11 GPIO_ACTIVE_LOW>; + #sound-dai-cells = <0x0>; + compatible = "everest,es8336"; + reg = <0x10>; + }; + }; +}; + +&gpio0 { + status = "okay"; +}; + +&gpio1 { + status = "okay"; +}; + +&gpio2 { + status = "okay"; +}; + +&gpio3 { + status = "okay"; +}; + +&gpio4 { + status = "okay"; +}; + +&gpio5 { + status = "okay"; +}; + +&watchdog0 { + status = "okay"; +}; + +&watchdog1 { + status = "okay"; +}; + +&pcie { + status = "okay"; +}; + +&usb3_0 { + status = "okay"; +}; + +&usb3_1 { + status = "okay"; +}; + +&usb2_0 { + dr_mode = "otg"; + status = "okay"; +}; + +&usb2_1 { + dr_mode = "peripheral"; + status = "okay"; +}; + +&usb2_2 { + dr_mode = "peripheral"; + status = "okay"; +}; + +&macb0 { + phy-mode = "sgmii"; + use-mii; + status = "okay"; +}; + +/* + * In demo board, M.2 interface may insert two types of disks: PCIE (nvme protocol) or SATA (ahci protocol), + * so here enable 'sata0' node by default to correspond to the sata drive case. Pin multiplexing exists + * in PCIE2 and SATA0, when used as a boot disk, the BIOS can automatically recognize these two conditions. + */ +&sata0 { + status = "okay"; +}; + +&sata1 { + status = "okay"; +}; + +&qspi0 { + status = "okay"; + + flash@0 { + status = "okay"; + }; +}; + +&spi2 { + global-cs = <1>; + status = "okay"; +}; + +&uart1 { + status = "okay"; +}; + +&uart2 { + status = "okay"; +}; + +&can0 { + status = "okay"; +}; + +&can1 { + status = "okay"; +}; + +&mmc0 { + bus-width = <0x00000008>; + max-frequency = <100000000>; + cap-mmc-hw-reset; + cap-mmc-highspeed; + mmc-hs200-1_8v; + no-sdio; + no-sd; + non-removable; + status = "okay"; +}; + +&mmc1 { + bus-width = <0x00000004>; + max-frequency = <50000000>; + cap-sdio-irq; + cap-sd-highspeed; + no-sdio; + no-mmc; + status = "okay"; +}; + +&i2s0 { + #sound-dai-cells = <0>; + dai-name = "phytium-i2s-lsd"; + status = "okay"; +}; + +&dc0 { + pipe_mask = /bits/ 8 <0x3>; + edp_mask = /bits/ 8 <0x0>; + status = "okay"; +}; + +&i2s_dp0 { + status = "okay"; +}; + +&i2s_dp1 { + status = "okay"; +}; + +&pmdk_dp { + num-dp = <2>; + dp-mask = /bits/ 8 <0x3>; + status = "okay"; +}; + +&rng0 { + status = "okay"; +}; diff --git a/target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/e2000q-edu-board.dts b/target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/e2000q-edu-board.dts new file mode 100755 index 000000000..02a97b686 --- /dev/null +++ b/target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/e2000q-edu-board.dts @@ -0,0 +1,343 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * DTS file for Phytium Pe2204 edu board + * + * Copyright (c) 2022-2023 Phytium Technology Co., Ltd. + * + * Hongmin Qi + * + * 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. + */ + +/dts-v1/; +/memreserve/ 0x80000000 0x10000; + +#include "pe2204.dtsi" + +/{ + model = "Pe2204 edu Board"; + compatible = "phytium,pe2204"; + + chosen { + stdout-path = "serial1:115200n8"; + }; + + memory@00{ + device_type = "memory"; + reg = <0x0 0x80000000 0x2 0x00000000>; + }; + + sound_card: sound { + compatible = "simple-audio-card"; + simple-audio-card,format = "i2s"; + simple-audio-card,name = "phytium,pe220x-i2s-audio"; + simple-audio-card,cpu { + sound-dai = <&i2s0>; + }; + simple-audio-card,codec { + sound-dai = <&codec0>; + }; + }; +}; + +&soc { + mio9: i2c@28026000 { + compatible = "phytium,i2c"; + reg = <0x0 0x28026000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + #address-cells = <0x1>; + #size-cells = <0x0>; + status = "okay"; + + rtc@68 { + compatible = "dallas,ds1339"; + reg = <0x68>; + }; + }; + + mio8: i2c@28024000 { + compatible = "phytium,i2c"; + reg = <0x0 0x28024000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + #address-cells = <0x1>; + #size-cells = <0x0>; + status = "okay"; + }; + + mio14: i2c@28030000 { + compatible = "phytium,i2c"; + reg = <0x0 0x28030000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + #address-cells = <0x1>; + #size-cells = <0x0>; + status = "okay"; + + codec0:es8336@10 { + det-gpios = <&gpio2 5 0>; + sel-gpios = <&gpio2 6 0>; + #sound-dai-cells = <0x0>; + compatible = "everest,es8336"; + reg = <0x10>; + mic-src = [30]; + }; + }; + + mio0: uart@28014000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0x0 0x28014000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz &sysclk_50mhz>; + clock-names = "uartclk", "apb_pclk"; + status = "okay"; + }; + + mio1: uart@28016000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0x0 0x28016000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz &sysclk_50mhz>; + clock-names = "uartclk", "apb_pclk"; + status = "okay"; + }; + + mio11: uart@2802A000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0x0 0x2802a000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz &sysclk_50mhz>; + clock-names = "uartclk", "apb_pclk"; + status = "okay"; + }; + + mio15: uart@28032000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0x0 0x28032000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz &sysclk_50mhz>; + clock-names = "uartclk", "apb_pclk"; + status = "okay"; + }; +}; + +&gpio2 { + status = "okay"; +}; + +&pcie { + status = "okay"; +}; + +&usb3_0 { + status = "okay"; +}; + +&usb3_1 { + //status = "okay"; +}; + +&usb2_0 { + dr_mode = "otg"; + status = "okay"; +}; + +&usb2_1 { + dr_mode = "host"; + //status = "okay"; +}; + +&usb2_2 { + dr_mode = "host"; + //status = "okay"; +}; + +&usb2_3 { + dr_mode = "host"; + status = "okay"; +}; + +&usb2_4 { + dr_mode = "host"; + //status = "okay"; +}; + +&macb0 { + phy-mode = "sgmii"; + use-mii; + //status = "okay"; +}; + +&macb1 { + phy-mode = "sgmii"; + use-mii; + //status = "okay"; +}; + +&macb2 { + phy-mode = "sgmii"; + use-mii; + //status = "okay"; +}; + +&macb3 { + phy-mode = "sgmii"; + use-mii; + status = "okay"; +}; + +&can0 { + status = "okay"; +}; + +&can1 { + status = "okay"; +}; + +&qspi0 { + status = "okay"; + + flash@0 { + status = "okay"; + }; +}; + +&spi0 { + global-cs = <1>; + status = "okay"; + + flash: w25q128@0 { + compatible = "jedec,spi-nor"; + spi-tx-bus-width = <1>; + spi-rx-bus-width = <1>; + spi-max-frequency = <12000000>; + reg = <0x00>; + status = "okay"; + }; +}; + +&mmc0 { + bus-width = <0x8>; + max-frequency = <50000000>; + cap-mmc-hw-reset; + cap-mmc-highspeed; + no-sdio; + no-sd; + non-removable; + status = "okay"; +}; + +&mmc1 { + bus-width = <0x4>; + max-frequency = <25000000>; + cap-sdio-irq; + cap-sd-highspeed; + status = "okay"; +}; + +&uart0 { + status = "okay"; +}; + +&uart1 { + status = "okay"; +}; + +&uart2 { + status = "okay"; +}; + +&uart3 { + status = "okay"; +}; + +&sata0 { + //status = "okay"; +}; + +&sata1 { + status = "okay"; +}; + +&dc0 { + status = "okay"; + pipe_mask = [01]; + edp_mask = [00]; +}; + +&i2s0 { + #sound-dai-cells = <0>; + dai-name = "phytium-i2s-lsd"; + status = "okay"; +}; + +&i2s_dp0 { + status = "okay"; +}; + +&pmdk_dp { + num-dp = <1>; + dp-mask = /bits/ 8 <0x1>; + status = "okay"; +}; + +&pwm0 { + phytium,db = <0 0 0 0 0 0>; + status = "okay"; +}; + +&pwm1 { + phytium,db = <0 0 0 0 0 0>; + status = "okay"; +}; + + +&gpio0 { + status = "okay"; +}; + +&gpio1 { + status = "okay"; +}; + +&gpio2 { + status = "okay"; +}; + +&gpio3 { + status = "okay"; +}; + +&gpio4 { + status = "okay"; +}; + +&gpio5 { + status = "okay"; +}; + +&keypad { + keypad,num-rows = <4>; + keypad,num-columns = <4>; + linux,keymap = <0x00000011 + 0x00010012 + 0x00020013 + 0x00030014 + 0x01000021 /*KEY_21*/ + 0x01010022 /*KEY_22*/ + 0x01020023 /*KEY_23*/ + 0x01030024 /*KEY_24*/ + 0x02000031 /*KEY_31*/ + 0x02010032 /*KEY_32*/ + 0x02020033 /*KEY_33*/ + 0x02030034 /*KEY_34*/ + 0x03000041 /*KEY_41*/ + 0x03010042 /*KEY_42*/ + 0x03020043 /*KEY_43*/ + 0x03030044 /*KEY_44*/>; + status = "okay"; +}; diff --git a/target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/e2000q-hanwei-board.dts b/target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/e2000q-hanwei-board.dts new file mode 100755 index 000000000..0c58438e3 --- /dev/null +++ b/target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/e2000q-hanwei-board.dts @@ -0,0 +1,215 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * DTS file for Phytium Pe2204 hanwei board + * + * Copyright (c) 2022-2023 Phytium Technology Co., Ltd. + * + * Hongmin Qi + * + * 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. + */ + +/dts-v1/; +/memreserve/ 0x80000000 0x10000; + +#include "pe2204.dtsi" + +/{ + model = "Pe2204 hanwei Board"; + compatible = "phytium,pe2204"; + + chosen { + stdout-path = "serial1:115200n8"; + }; + + memory@00{ + device_type = "memory"; + reg = <0x0 0x80000000 0x2 0x00000000>; + }; +}; + +&soc { + mio3: uart@2801a000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0x0 0x2801a000 0x0 0x1000>; + interrupts = <0x0 0x5f 0x4>; + clocks = <&sysclk_50mhz &sysclk_50mhz>; + clock-names = "uartclk", "apb_pclk"; + status = "okay"; + }; + + mio8: uart@28024000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0x0 0x28024000 0x0 0x1000>; + interrupts = <0x0 0x64 0x4>; + clocks = <&sysclk_50mhz &sysclk_50mhz>; + clock-names = "uartclk", "apb_pclk"; + status = "okay"; + }; + + mio10: uart@28028000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0x0 0x28028000 0x0 0x1000>; + interrupts = <0x0 0x66 0x4>; + clocks = <&sysclk_50mhz &sysclk_50mhz>; + clock-names = "uartclk", "apb_pclk"; + status = "okay"; + }; + + mio14: uart@28030000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0x0 0x28030000 0x0 0x1000>; + interrupts = <0x0 0x6a 0x4>; + clocks = <&sysclk_50mhz &sysclk_50mhz>; + clock-names = "uartclk", "apb_pclk"; + status = "okay"; + }; + + mio15: uart@28032000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0x0 0x28032000 0x0 0x1000>; + interrupts = <0x0 0x6b 0x4>; + clocks = <&sysclk_50mhz &sysclk_50mhz>; + clock-names = "uartclk", "apb_pclk"; + status = "okay"; + }; +}; + +&pcie { + status = "okay"; +}; + +&usb3_0 { + status = "okay"; +}; + +&usb3_1 { + status = "okay"; +}; + +&usb2_0 { + dr_mode = "otg"; + status = "okay"; +}; + +&usb2_3 { + dr_mode = "host"; + status = "okay"; +}; + +&usb2_4 { + dr_mode = "host"; + status = "okay"; +}; + +&macb0 { + phy-mode = "sgmii"; + use-mii; + status = "okay"; +}; + +&macb1 { + phy-mode = "sgmii"; + use-mii; + status = "okay"; +}; + +&macb2 { + phy-mode = "rgmii"; + use-mii; + status = "okay"; +}; + +&macb3 { + phy-mode = "rgmii"; + use-mii; + status = "okay"; +}; + +&can0 { + status = "okay"; +}; + +&can1 { + status = "okay"; +}; + +&qspi0 { + status = "okay"; + + flash@0 { + status = "okay"; + }; +}; + +&spi0 { + global-cs = <1>; + status = "disabled"; +}; + +&mmc0 { + bus-width = <8>; + max-frequency = <25000000>; + cap-mmc-hw-reset; + cap-mmc-highspeed; + no-sdio; + no-sd; + non-removable; + status = "disabled"; +}; + +&mmc1 { + status = "okay"; +}; + +&uart0 { + status = "okay"; +}; + +&uart1 { + status = "okay"; +}; + +&uart2 { + status = "okay"; +}; + +&uart3 { + status = "okay"; +}; + +&sata0 { + status = "okay"; +}; + +&sata1 { + status = "okay"; +}; + +&gpio0 { + status = "okay"; +}; + +&gpio1 { + status = "okay"; +}; + +&gpio2 { + status = "okay"; +}; + +&gpio3 { + status = "okay"; +}; + +&gpio4 { + status = "okay"; +}; + +&gpio5 { + status = "okay"; +}; + diff --git a/target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/e2000q-miniitx-board.dts b/target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/e2000q-miniitx-board.dts new file mode 100644 index 000000000..81218adbe --- /dev/null +++ b/target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/e2000q-miniitx-board.dts @@ -0,0 +1,329 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * DTS file for Phytium miniITX-Pe2204 development board. + * + * Copyright (c) 2022-2023 Phytium Technology Co., Ltd. + * + * Shaojun Yang + * + * 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. + */ + +/dts-v1/; +/memreserve/ 0x80000000 0x10000; +/memreserve/ 0xf4000000 0x4000000; + +#include "pe2204.dtsi" + +/{ + model = "miniITX-Pe2204 Board"; + compatible = "phytium,pe2204"; + + chosen { + stdout-path = "serial1:115200n8"; + }; + aliases { + serial4 = &mio0; + serial5 = &mio1; + serial6 = &mio8; + serial7 = &mio11; + serial8 = &mio15; + }; + + memory@00{ + device_type = "memory"; + reg = <0x0 0x80000000 0x2 0x00000000>; + }; + + sound_card: sound { + compatible = "simple-audio-card"; + simple-audio-card,format = "i2s"; + simple-audio-card,name = "phytium,pe220x-i2s-audio"; + simple-audio-card,cpu { + sound-dai = <&i2s0>; + }; + simple-audio-card,codec{ + sound-dai = <&codec0>; + }; + }; +}; + +&soc { + mio9: i2c@28026000 { + compatible = "phytium,i2c"; + reg = <0x0 0x28026000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + rtc@68 { + compatible = "dallas,ds1339"; + reg = <0x68>; + }; + }; + + mio14: i2c@28030000 { + compatible = "phytium,i2c"; + reg = <0x0 0x28030000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + codec0: es8336@10 { + det-gpios = <&gpio2 5 0>; + sel-gpios = <&gpio2 6 0>; + #sound-dai-cells = <0>; + compatible = "everest,es8336"; + reg = <0x10>; + mic-src = [20]; + }; + }; + + + mio0: uart@28014000 { + compatible = "arm,pl011","arm,primecell"; + reg = <0x0 0x28014000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz &sysclk_50mhz>; + clock-names = "uartclk", "apb_pclk"; + status = "okay"; + }; + + mio1: uart@28016000 { + compatible = "arm,pl011","arm,primecell"; + reg = <0x0 0x28016000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz &sysclk_50mhz>; + clock-names = "uartclk", "apb_pclk"; + status = "okay"; + }; + + mio8: uart@28024000 { + compatible = "arm,pl011","arm,primecell"; + reg = <0x0 0x28024000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz &sysclk_50mhz>; + clock-names = "uartclk", "apb_pclk"; + status = "okay"; + }; + + mio11: uart@2802A000 { + compatible = "arm,pl011","arm,primecell"; + reg = <0x0 0x2802A000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz &sysclk_50mhz>; + clock-names = "uartclk", "apb_pclk"; + status = "okay"; + }; + + mio15: uart@28032000 { + compatible = "arm,pl011","arm,primecell"; + reg = <0x0 0x28032000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz &sysclk_50mhz>; + clock-names = "uartclk", "apb_pclk"; + status = "okay"; + }; +}; + +&gpio0 { + status = "okay"; +}; + +&gpio1 { + status = "okay"; +}; + +&gpio2 { + status = "okay"; +}; + +&gpio3 { + status = "okay"; +}; + +&gpio4 { + status = "okay"; +}; + +&gpio5 { + status = "okay"; +}; + +&watchdog0 { + status = "okay"; +}; + +&watchdog1 { + status = "okay"; +}; + +&pcie { + status = "okay"; +}; + +&sata1 { + status = "okay"; +}; + +&usb3_0 { + status = "okay"; +}; + +&usb3_1 { + status = "okay"; +}; + +&usb2_0 { + dr_mode = "otg"; + status = "okay"; +}; + +&usb2_1 { + dr_mode = "peripheral"; + status = "disabled"; +}; + +&usb2_2 { + dr_mode = "peripheral"; + status = "disabled"; +}; + +&usb2_3 { + dr_mode = "host"; + status = "okay"; +}; + +&usb2_4 { + dr_mode = "host"; + status = "okay"; +}; + +&macb0 { + phy-mode = "sgmii"; + use-mii; + status = "okay"; +}; + +&macb2 { + phy-mode = "rgmii"; + use-mii; + status = "okay"; +}; + +&dc0 { + reg = <0x0 0x32000000 0x0 0x8000>, + <0x0 0xf4000000 0x0 0x4000000>; // (optional) + pipe_mask = [03]; + edp_mask = [00]; + status = "okay"; +}; + +&i2s0 { + #sound-dai-cells = <0>; + dai-name = "phytium-i2s-lsd"; + status = "okay"; +}; + +&i2s_dp0 { + dai-name = "phytium-i2s-dp0"; + status = "okay"; +}; + +&qspi0 { + status = "okay"; + + flash@0 { + status = "okay"; + }; +}; + +&spi0 { + global-cs = <1>; + status = "disabled"; + + flash: w25q128@0 { + compatible = "winbond,w25q128", "jedec,spi-nor"; + spi-tx-bus-width = <1>; + spi-rx-bus-width = <1>; + spi-max-frequency = <12000000>; + reg = <0x00>; + status = "disabled"; + }; +}; + +&spi1 { + global-cs = <1>; + status = "disabled"; +}; + +&spi2 { + global-cs = <1>; + status = "disabled"; +}; + +&mmc0 { + bus-width = <0x00000004>; + max-frequency = <50000000>; + cap-sdio-irq; + cap-sd-highspeed; + no-mmc; + status = "okay"; +}; + +&mmc1 { + bus-width = <0x00000008>; + max-frequency = <50000000>; + cap-mmc-hw-reset; + cap-mmc-highspeed; + no-sdio; + no-sd; + non-removable; + status = "disabled"; +}; + +&pwm0 { + phytium,db = <0 0 0 1000 1000 0>; + status = "okay"; +}; + +&pwm1 { + phytium,db = <0 0 0 1000 1000 0>; + status = "okay"; +}; + +&uart0 { + status = "okay"; +}; + +&uart1 { + status = "okay"; +}; + +&uart2 { + status = "okay"; +}; + +&uart3 { + status = "okay"; +}; + +&can0 { + status = "okay"; +}; + +&can1 { + status = "okay"; +}; + +&rng0 { + status = "okay"; +}; + diff --git a/target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/e2000q-vpx-board.dts b/target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/e2000q-vpx-board.dts new file mode 100755 index 000000000..3924c5bce --- /dev/null +++ b/target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/e2000q-vpx-board.dts @@ -0,0 +1,322 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * DTS file for Phytium Pe2204 vpx board. + * + * Copyright (c) 2022-2023 Phytium Technology Co., Ltd. + * + * Tianyu Liu + * + * 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. + */ + +/dts-v1/; +/memreserve/ 0x80000000 0x10000; + +#include "pe2204.dtsi" + +/{ + model = "Pe2204 vpx board"; + compatible = "phytium,pe2204"; + + chosen { + stdout-path = "serial1:115200n8"; + }; + + memory@00{ + device_type = "memory"; + reg = <0x0 0x80000000 0x2 0x00000000>; + }; + aliases { + serial4 = &mio3; + serial5 = &mio8; + serial6 = &mio10; + serial7 = &mio11; + serial8 = &mio15; + }; + + sound_card: sound { + compatible = "simple-audio-card"; + simple-audio-card,format = "i2s"; + simple-audio-card,name = "phytium,pe220x-i2s-audio"; + simple-audio-card,cpu { + sound-dai = <&i2s0>; + }; + simple-audio-card,codec{ + sound-dai = <&codec0>; + }; + }; +}; + +&soc { + mio9: i2c@28026000 { + compatible = "phytium,i2c"; + reg = <0x0 0x28026000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + rtc@68 { + compatible = "dallas,ds1339"; + reg = <0x68>; + }; + }; + + mio14: i2c@28030000 { + compatible = "phytium,i2c"; + reg = <0x0 0x28030000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + codec0: es8336@10 { + det-gpios = <&gpio2 5 0>; + sel-gpios = <&gpio2 6 0>; + #sound-dai-cells = <0>; + compatible = "everest,es8336"; + reg = <0x10>; + mic-src = [30]; + }; + }; + + mio3: uart@2801A000 { + compatible = "arm,pl011","arm,primecell"; + reg = <0x0 0x2801A000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz &sysclk_50mhz>; + clock-names = "uartclk", "apb_pclk"; + status = "okay"; + }; + + mio8: uart@28024000 { + compatible = "arm,pl011","arm,primecell"; + reg = <0x0 0x28024000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz &sysclk_50mhz>; + clock-names = "uartclk", "apb_pclk"; + status = "okay"; + }; + + mio10: uart@28028000 { + compatible = "arm,pl011","arm,primecell"; + reg = <0x0 0x28028000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz &sysclk_50mhz>; + clock-names = "uartclk", "apb_pclk"; + status = "okay"; + }; + + mio11: uart@2802A000 { + compatible = "arm,pl011","arm,primecell"; + reg = <0x0 0x2802A000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz &sysclk_50mhz>; + clock-names = "uartclk", "apb_pclk"; + status = "okay"; + }; + + mio13: uart@2802E000 { + compatible = "arm,pl011","arm,primecell"; + reg = <0x0 0x2802E000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz &sysclk_50mhz>; + clock-names = "uartclk", "apb_pclk"; + status = "okay"; + }; + + mio15: uart@28032000 { + compatible = "arm,pl011","arm,primecell"; + reg = <0x0 0x28032000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz &sysclk_50mhz>; + clock-names = "uartclk", "apb_pclk"; + status = "okay"; + }; +}; + +&gpio0 { + status = "okay"; +}; + +&gpio1 { + status = "okay"; +}; + +&gpio2 { + status = "okay"; +}; + +&gpio3 { + status = "okay"; +}; + +&gpio4 { + status = "okay"; +}; + +&gpio5 { + status = "okay"; +}; + +&pcie { + status = "okay"; +}; + +&sata0 { + status = "okay"; +}; + +&sata1 { + status = "okay"; +}; + +&usb3_0 { + status = "okay"; +}; + +&usb3_1 { + status = "okay"; +}; + +&usb2_0 { + dr_mode = "otg"; + status = "okay"; +}; + +&usb2_1 { + dr_mode = "host"; + status = "disabled"; +}; + +&usb2_2 { + dr_mode = "host"; + status = "disabled"; +}; + +&usb2_3 { + dr_mode = "host"; + status = "okay"; +}; + +&usb2_4 { + dr_mode = "host"; + status = "okay"; +}; + +&macb0 { + phy-mode = "sgmii"; + use-mii; + status = "okay"; +}; + +&macb2 { + phy-mode = "rgmii"; + use-mii; + force-phy-mode; + status = "okay"; +}; + +&macb3 { + phy-mode = "rgmii"; + use-mii; + status = "okay"; +}; + +&dc0 { + pipe_mask = [03]; + edp_mask = [00]; + status = "okay"; +}; + +&i2s0 { + #sound-dai-cells = <0>; + dai-name = "phytium-i2s-lsd"; + status = "okay"; +}; + +&i2s_dp0 { + status = "okay"; +}; + +&i2s_dp1 { + status = "okay"; +}; + +&pmdk_dp { + num-dp = <2>; + dp-mask = /bits/ 8 <0x3>; + status = "okay"; +}; + +&qspi0 { + status = "okay"; + + flash@0 { + status = "okay"; + }; +}; + +&spi0 { + global-cs = <1>; + status = "disabled"; + + flash: w25q128@0 { + compatible = "winbond,w25q128", "jedec,spi-nor"; + spi-tx-bus-width = <1>; + spi-rx-bus-width = <1>; + spi-max-frequency = <12000000>; + reg = <0x00>; + status = "disabled"; + }; +}; + +&spi2 { + global-cs = <1>; + status = "disabled"; +}; + +&mmc0 { + bus-width = <0x00000008>; + max-frequency = <25000000>; + cap-mmc-hw-reset; + cap-mmc-highspeed; + no-sdio; + no-sd; + non-removable; + status = "okay"; +}; + +&uart0 { + status = "okay"; +}; + +&uart1 { + status = "okay"; +}; + +&uart2 { + status = "okay"; +}; + +&uart3 { + status = "okay"; +}; + +&can0 { + status = "okay"; +}; + +&can1 { + status = "okay"; +}; + +&rng0 { + status = "okay"; +}; + diff --git a/target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/e2000s-demo-board.dts b/target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/e2000s-demo-board.dts new file mode 100644 index 000000000..047d1c5d5 --- /dev/null +++ b/target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/e2000s-demo-board.dts @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * DTS file for Phytium Pe2201 demo board + * + * Copyright (c) 2022-2023 Phytium Technology Co., Ltd. + * + * 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. + */ + +/dts-v1/; +/memreserve/ 0x80000000 0x10000; + +#include "pe2201.dtsi" + +/{ + model = "Pe2201 DEMO DDR4"; + compatible = "phytium,pe2201"; + + chosen { + stdout-path = "serial1:115200n8"; + }; + + memory@00{ + device_type = "memory"; + reg = <0x0 0x80000000 0x0 0x80000000>; + }; +}; + +&soc { + mio9: i2c@28026000 { + compatible = "phytium,i2c"; + reg = <0x0 0x28026000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + }; +}; + +&pcie { + status = "okay"; +}; + +&usb2_0 { + dr_mode = "otg"; + status = "okay"; +}; + +&usb2_1 { + dr_mode = "peripheral"; + status = "okay"; +}; + +&usb2_2 { + dr_mode = "peripheral"; + status = "okay"; +}; + +&macb0 { + phy-mode = "sgmii"; + use-mii; + status = "okay"; +}; + +&qspi0 { + status = "okay"; + + flash@0 { + status = "okay"; + }; +}; + +&spi2 { + global-cs = <1>; + status = "okay"; +}; + +&uart1 { + status = "okay"; +}; + +&uart2 { + status = "okay"; +}; + +&mmc0 { + status = "okay"; +}; + +&mmc1 { + status = "okay"; +}; + +&rng0 { + status = "okay"; +}; diff --git a/target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/pe2201.dtsi b/target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/pe2201.dtsi new file mode 100644 index 000000000..b7576e684 --- /dev/null +++ b/target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/pe2201.dtsi @@ -0,0 +1,232 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * dts file for Phytium Pe2201 SoC + * + * Copyright (c) 2022-2023 Phytium Technology Co., Ltd. + */ + +#include "pe220x.dtsi" + +/ { + compatible = "phytium,pe2201"; + + aliases { + ethernet0 = &macb0; + ethernet1 = &macb1; + }; +}; + +&cpu { + cpu0: cpu@0 { + device_type = "cpu"; + compatible = "phytium,ftc310", "arm,armv8"; + reg = <0x0 0x200>; + enable-method = "psci"; + clocks = <&scmi_dvfs 2>; + }; +}; + +&soc { + i2c0: i2c@28011000 { + compatible = "phytium,i2c"; + reg = <0x0 0x28011000 0x0 0x1000>; + interrupts = ; + interrupt-names = "smbus_alert"; + clocks = <&sysclk_50mhz>; + status = "disabled"; + }; + + i2c1: i2c@28012000 { + compatible = "phytium,i2c"; + reg = <0x0 0x28012000 0x0 0x1000>; + interrupts = ; + interrupt-names = "smbus_alert"; + clocks = <&sysclk_50mhz>; + status = "disabled"; + }; + + i2c2: i2c@28013000 { + compatible = "phytium,i2c"; + reg = <0x0 0x28013000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + status = "disabled"; + }; + + onewire0: onewire@2803f000 { + compatible = "phytium,w1"; + reg = <0x0 0x2803f000 0x0 0x1000>; + interrupts = ; + status = "disabled"; + }; + + pwm2: pwm@2804c000 { + compatible = "phytium,pwm"; + reg = <0x0 0x2804c000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + status = "disabled"; + }; + + pwm3: pwm@2804d000 { + compatible = "phytium,pwm"; + reg = <0x0 0x2804d000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + status = "disabled"; + }; + + pwm4: pwm@2804e000 { + compatible = "phytium,pwm"; + reg = <0x0 0x2804e000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + status = "disabled"; + }; + + pwm5: pwm@2804f000 { + compatible = "phytium,pwm"; + reg = <0x0 0x2804f000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + status = "disabled"; + }; + + pwm6: pwm@28050000 { + compatible = "phytium,pwm"; + reg = <0x0 0x28050000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + status = "disabled"; + }; + + pwm7: pwm@28051000 { + compatible = "phytium,pwm"; + reg = <0x0 0x28051000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + status = "disabled"; + }; + + adc0: adc@2807b000 { + compatible = "phytium,adc"; + reg = <0x0 0x2807b000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + status = "disabled"; + + #address-cells = <1>; + #size-cells = <0>; + + channel@0 { + reg = <0>; + }; + channel@1 { + reg = <1>; + }; + channel@2 { + reg = <2>; + }; + channel@3 { + reg = <3>; + }; + channel@4 { + reg = <4>; + }; + channel@5 { + reg = <5>; + }; + channel@6 { + reg = <6>; + }; + channel@7 { + reg = <7>; + }; + }; + + adc1: adc@2807c000 { + compatible = "phytium,adc"; + reg = <0x0 0x2807c000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + status = "disabled"; + + #address-cells = <1>; + #size-cells = <0>; + + channel@0 { + reg = <0>; + }; + channel@1 { + reg = <1>; + }; + channel@2 { + reg = <2>; + }; + channel@3 { + reg = <3>; + }; + channel@4 { + reg = <4>; + }; + channel@5 { + reg = <5>; + }; + channel@6 { + reg = <6>; + }; + channel@7 { + reg = <7>; + }; + }; + + sgpio: sgpio@2807d000 { + compatible = "phytium,sgpio"; + reg = <0x0 0x2807d000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + ngpios = <96>; + bus-frequency = <48000>; + gpio-controller; + #gpio-cells = <2>; + status = "disabled"; + }; + + macb0: ethernet@32010000 { + compatible = "cdns,phytium-gem-1.0"; + reg = <0x0 0x32010000 0x0 0x2000>; + interrupts = , + , + , + ; + clock-names = "pclk", "hclk", "tx_clk", "tsu_clk"; + clocks = <&sysclk_250mhz>, <&sysclk_48mhz>, <&sysclk_48mhz>, <&sysclk_250mhz>; + magic-packet; + status = "disabled"; + }; + + macb1: ethernet@32012000 { + compatible = "cdns,phytium-gem-1.0"; + reg = <0x0 0x32012000 0x0 0x2000>; + interrupts = , + , + , + ; + clock-names = "pclk", "hclk", "tx_clk", "tsu_clk"; + clocks = <&sysclk_250mhz>, <&sysclk_48mhz>, <&sysclk_48mhz>, <&sysclk_250mhz>; + magic-packet; + status = "disabled"; + }; + + jpeg0: jpeg@32b32000 { + compatible = "phytium,jpeg"; + reg = <0x0 0x32b32000 0 0x1000>, + <0x0 0x28072000 0 0x30>, + <0x0 0x28073000 0 0x30>; + interrupts = , + , + ; + phytium,ocm-buf-addr = <0x30c40000 0x30c60000>; + status = "disabled"; + }; +}; diff --git a/target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/pe2202.dtsi b/target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/pe2202.dtsi new file mode 100644 index 000000000..9f19b71be --- /dev/null +++ b/target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/pe2202.dtsi @@ -0,0 +1,184 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * dts file for Phytium Pe2202 SoC + * + * Copyright (c) 2022-2023 Phytium Technology Co., Ltd. + */ + +#include "pe220x.dtsi" + +/ { + compatible = "phytium,pe2202"; + + aliases { + ethernet0 = &macb0; + ethernet1 = &macb1; + ethernet2 = &macb2; + ethernet3 = &macb3; + }; +}; + +&cpu { + cpu-map { + cluster0 { + core0 { + cpu = <&cpu_l0>; + }; + + core1 { + cpu = <&cpu_l1>; + }; + }; + }; + + cpu_l0: cpu@0 { + device_type = "cpu"; + compatible = "phytium,ftc310", "arm,armv8"; + reg = <0x0 0x200>; + enable-method = "psci"; + clocks = <&scmi_dvfs 2>; + }; + + cpu_l1: cpu@1 { + device_type = "cpu"; + compatible = "phytium,ftc310", "arm,armv8"; + reg = <0x0 0x201>; + enable-method = "psci"; + clocks = <&scmi_dvfs 2>; + }; +}; + +&soc { + hda0: hda@28006000 { + compatible = "phytium,hda"; + reg = <0x0 0x28006000 0x0 0x1000>; + interrupts = ; + status = "disabled"; + }; + + i2s0: i2s@28009000 { + compatible = "phytium,i2s"; + reg = <0x0 0x28009000 0x0 0x1000>, + <0x0 0x28005000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_600mhz>; + clock-names = "i2s_clk"; + status = "disabled"; + }; + + can0: can@2800a000 { + compatible = "phytium,canfd"; + reg = <0x0 0x2800a000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_200mhz>; + clock-names = "can_clk"; + tx-fifo-depth = <64>; + rx-fifo-depth = <64>; + status = "disabled"; + }; + + can1: can@2800b000 { + compatible = "phytium,canfd"; + reg = <0x0 0x2800b000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_200mhz>; + clock-names = "can_clk"; + tx-fifo-depth = <64>; + rx-fifo-depth = <64>; + status = "disabled"; + }; + + keypad: keypad@2807a000 { + compatible = "phytium,keypad"; + reg = <0x0 0x2807a000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + status = "disabled"; + }; + + usb3_0: usb3@31a08000 { + compatible = "phytium,pe220x-xhci"; + reg = <0x0 0x31a08000 0x0 0x18000>; + interrupts = ; + status = "disabled"; + }; + + usb3_1: usb3@31a28000 { + compatible = "phytium,pe220x-xhci"; + reg = <0x0 0x31a28000 0x0 0x18000>; + interrupts = ; + status = "disabled"; + }; + + sata0: sata@31a40000 { + compatible = "generic-ahci"; + reg = <0x0 0x31a40000 0x0 0x1000>; + interrupts = ; + status = "disabled"; + }; + + sata1: sata@32014000 { + compatible = "generic-ahci"; + reg = <0x0 0x32014000 0x0 0x1000>; + interrupts = ; + status = "disabled"; + }; + + macb0: ethernet@3200c000 { + compatible = "cdns,phytium-gem-1.0"; + reg = <0x0 0x3200c000 0x0 0x2000>; + interrupts = , + , + , + , + , + , + , + ; + clock-names = "pclk", "hclk", "tx_clk", "tsu_clk"; + clocks = <&sysclk_250mhz>, <&sysclk_48mhz>, <&sysclk_48mhz>, <&sysclk_250mhz>; + magic-packet; + status = "disabled"; + }; + + macb1: ethernet@3200e000 { + compatible = "cdns,phytium-gem-1.0"; + reg = <0x0 0x3200e000 0x0 0x2000>; + interrupts = , + , + , + ; + clock-names = "pclk", "hclk", "tx_clk", "tsu_clk"; + clocks = <&sysclk_250mhz>, <&sysclk_48mhz>, <&sysclk_48mhz>, <&sysclk_250mhz>; + magic-packet; + status = "disabled"; + }; + + macb2: ethernet@32010000 { + compatible = "cdns,phytium-gem-1.0"; + reg = <0x0 0x32010000 0x0 0x2000>; + interrupts = , + , + , + ; + clock-names = "pclk", "hclk", "tx_clk", "tsu_clk"; + clocks = <&sysclk_250mhz>, <&sysclk_48mhz>, <&sysclk_48mhz>, <&sysclk_250mhz>; + magic-packet; + use-mii; + status = "disabled"; + }; + + macb3: ethernet@32012000 { + compatible = "cdns,phytium-gem-1.0"; + reg = <0x0 0x32012000 0x0 0x2000>; + interrupts = , + , + , + ; + clock-names = "pclk", "hclk", "tx_clk", "tsu_clk"; + clocks = <&sysclk_250mhz>, <&sysclk_48mhz>, <&sysclk_48mhz>, <&sysclk_250mhz>; + magic-packet; + use-mii; + status = "disabled"; + }; +}; diff --git a/target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/pe2204.dtsi b/target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/pe2204.dtsi new file mode 100644 index 000000000..59dc498cd --- /dev/null +++ b/target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/pe2204.dtsi @@ -0,0 +1,216 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * dts file for Phytium Pe2204 SoC + * + * Copyright (c) 2022-2023 Phytium Technology Co., Ltd. + */ + +#include "pe220x.dtsi" + +/ { + compatible = "phytium,pe2204"; + + aliases { + ethernet0 = &macb0; + ethernet1 = &macb1; + ethernet2 = &macb2; + ethernet3 = &macb3; + }; +}; + +&cpu { + cpu-map { + cluster0 { + core0 { + cpu = <&cpu_b0>; + }; + }; + + cluster1 { + core0 { + cpu = <&cpu_b1>; + }; + }; + + cluster2 { + core0 { + cpu = <&cpu_l0>; + }; + + core1 { + cpu = <&cpu_l1>; + }; + }; + }; + + cpu_l0: cpu@0 { + device_type = "cpu"; + compatible = "phytium,ftc310", "arm,armv8"; + reg = <0x0 0x200>; + enable-method = "psci"; + clocks = <&scmi_dvfs 2>; + capacity-dmips-mhz = <2850>; + }; + + cpu_l1: cpu@1 { + device_type = "cpu"; + compatible = "phytium,ftc310", "arm,armv8"; + reg = <0x0 0x201>; + enable-method = "psci"; + clocks = <&scmi_dvfs 2>; + capacity-dmips-mhz = <2850>; + }; + + cpu_b0: cpu@100 { + device_type = "cpu"; + compatible = "phytium,ftc664", "arm,armv8"; + reg = <0x0 0x0>; + enable-method = "psci"; + clocks = <&scmi_dvfs 0>; + capacity-dmips-mhz = <5660>; + }; + + cpu_b1: cpu@101 { + device_type = "cpu"; + compatible = "phytium,ftc664", "arm,armv8"; + reg = <0x0 0x100>; + enable-method = "psci"; + clocks = <&scmi_dvfs 1>; + capacity-dmips-mhz = <5660>; + }; +}; + +&soc { + hda0: hda@28006000 { + compatible = "phytium,hda"; + reg = <0x0 0x28006000 0x0 0x1000>; + interrupts = ; + status = "disabled"; + }; + + i2s0: i2s@28009000 { + compatible = "phytium,i2s"; + reg = <0x0 0x28009000 0x0 0x1000>, + <0x0 0x28005000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_600mhz>; + clock-names = "i2s_clk"; + status = "disabled"; + }; + + can0: can@2800a000 { + compatible = "phytium,canfd"; + reg = <0x0 0x2800a000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_200mhz>; + clock-names = "can_clk"; + tx-fifo-depth = <64>; + rx-fifo-depth = <64>; + status = "disabled"; + }; + + can1: can@2800b000 { + compatible = "phytium,canfd"; + reg = <0x0 0x2800b000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_200mhz>; + clock-names = "can_clk"; + tx-fifo-depth = <64>; + rx-fifo-depth = <64>; + status = "disabled"; + }; + + keypad: keypad@2807a000 { + compatible = "phytium,keypad"; + reg = <0x0 0x2807a000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + status = "disabled"; + }; + + usb3_0: usb3@31a08000 { + compatible = "phytium,pe220x-xhci"; + reg = <0x0 0x31a08000 0x0 0x18000>; + interrupts = ; + status = "disabled"; + }; + + usb3_1: usb3@31a28000 { + compatible = "phytium,pe220x-xhci"; + reg = <0x0 0x31a28000 0x0 0x18000>; + interrupts = ; + status = "disabled"; + }; + + sata0: sata@31a40000 { + compatible = "generic-ahci"; + reg = <0x0 0x31a40000 0x0 0x1000>; + interrupts = ; + status = "disabled"; + }; + + sata1: sata@32014000 { + compatible = "generic-ahci"; + reg = <0x0 0x32014000 0x0 0x1000>; + interrupts = ; + status = "disabled"; + }; + + macb0: ethernet@3200c000 { + compatible = "cdns,phytium-gem-1.0"; + reg = <0x0 0x3200c000 0x0 0x2000>; + interrupts = , + , + , + , + , + , + , + ; + clock-names = "pclk", "hclk", "tx_clk", "tsu_clk"; + clocks = <&sysclk_250mhz>, <&sysclk_48mhz>, <&sysclk_48mhz>, <&sysclk_250mhz>; + magic-packet; + support-tsn; + status = "disabled"; + }; + + macb1: ethernet@3200e000 { + compatible = "cdns,phytium-gem-1.0"; + reg = <0x0 0x3200e000 0x0 0x2000>; + interrupts = , + , + , + ; + clock-names = "pclk", "hclk", "tx_clk", "tsu_clk"; + clocks = <&sysclk_250mhz>, <&sysclk_48mhz>, <&sysclk_48mhz>, <&sysclk_250mhz>; + magic-packet; + status = "disabled"; + }; + + macb2: ethernet@32010000 { + compatible = "cdns,phytium-gem-1.0"; + reg = <0x0 0x32010000 0x0 0x2000>; + interrupts = , + , + , + ; + clock-names = "pclk", "hclk", "tx_clk", "tsu_clk"; + clocks = <&sysclk_250mhz>, <&sysclk_48mhz>, <&sysclk_48mhz>, <&sysclk_250mhz>; + magic-packet; + status = "disabled"; + }; + + macb3: ethernet@32012000 { + compatible = "cdns,phytium-gem-1.0"; + reg = <0x0 0x32012000 0x0 0x2000>; + interrupts = , + , + , + ; + clock-names = "pclk", "hclk", "tx_clk", "tsu_clk"; + clocks = <&sysclk_250mhz>, <&sysclk_48mhz>, <&sysclk_48mhz>, <&sysclk_250mhz>; + magic-packet; + status = "disabled"; + }; + +}; diff --git a/target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/pe220x.dtsi b/target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/pe220x.dtsi new file mode 100644 index 000000000..a03fe3236 --- /dev/null +++ b/target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/pe220x.dtsi @@ -0,0 +1,1001 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * dts file for Phytium Pe220x SoC + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#include + +/ { + compatible = "phytium,pe220x"; + interrupt-parent = <&gic>; + #address-cells = <2>; + #size-cells = <2>; + + aliases { + serial0 = &uart0; + serial1 = &uart1; + serial2 = &uart2; + serial3 = &uart3; + }; + + psci { + compatible = "arm,psci-1.0"; + method = "smc"; + cpu_suspend = <0xc4000001>; + cpu_off = <0x84000002>; + cpu_on = <0xc4000003>; + sys_poweroff = <0x84000008>; + sys_reset = <0x84000009>; + }; + + firmware { + scmi: scmi { + compatible = "arm,scmi"; + mboxes = <&mbox 0>; + mbox-names = "tx"; + shmem = <&cpu_scp_hpri>; + #address-cells = <1>; + #size-cells = <0>; + + scmi_dvfs: protocol@13 { + reg = <0x13>; + #clock-cells = <1>; + }; + + scmi_sensors0: protocol@15 { + reg = <0x15>; + #thermal-sensor-cells = <1>; + }; + }; + optee { + compatible = "linaro,optee-tz"; + method = "smc"; + }; + }; + + thermal-zones { + sensor0 { + polling-delay-passive = <100>; + polling-delay = <1000>; + thermal-sensors = <&scmi_sensors0 0>; + }; + + sensor1 { + polling-delay-passive = <100>; + polling-delay = <1000>; + thermal-sensors = <&scmi_sensors0 1>; + }; + }; + + cpu: cpus { + #address-cells = <0x2>; + #size-cells = <0x0>; + }; + + gic: interrupt-controller@30800000 { + compatible = "arm,gic-v3"; + #interrupt-cells = <3>; + #address-cells = <2>; + #size-cells = <2>; + ranges; + interrupt-controller; + reg = <0x0 0x30800000 0 0x20000>, /* GICD */ + <0x0 0x30880000 0 0x80000>, /* GICR */ + <0x0 0x30840000 0 0x10000>, /* GICC */ + <0x0 0x30850000 0 0x10000>, /* GICH */ + <0x0 0x30860000 0 0x10000>; /* GICV */ + interrupts = ; + + its: gic-its@30820000 { + compatible = "arm,gic-v3-its"; + msi-controller; + reg = <0x0 0x30820000 0x0 0x20000>; + }; + }; + + pmu { + compatible = "arm,armv8-pmuv3"; + interrupts = ; + }; + + timer { + compatible = "arm,armv8-timer"; + interrupts = , + , + , + ; + clock-frequency = <50000000>; + }; + + clocks { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + sysclk_48mhz: clk48mhz { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <48000000>; + }; + + sysclk_50mhz: clk50mhz { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <50000000>; + }; + + sysclk_100mhz: clk100mhz { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <100000000>; + }; + + sysclk_200mhz: clk200mhz { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <200000000>; + }; + + sysclk_250mhz: clk250mhz { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <250000000>; + }; + + sysclk_300mhz: clk300mhz { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <300000000>; + }; + + sysclk_600mhz: clk600mhz { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <600000000>; + }; + + sysclk_1200mhz: clk1200mhz { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <1200000000>; + }; + }; + + smmu: iommu@30000000 { + compatible = "arm,smmu-v3"; + reg = <0x0 0x30000000 0x0 0x800000>; + interrupts = , + , + , + ; + interrupt-names = "eventq", "priq", "cmdq-sync", "gerror"; + dma-coherent; + #iommu-cells = <1>; + }; + + soc: soc { + compatible = "simple-bus"; + #address-cells = <2>; + #size-cells = <2>; + dma-coherent; + ranges; + + mmc0: mmc@28000000 { + compatible = "phytium,mci"; + reg = <0x0 0x28000000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_1200mhz>; + clock-names = "phytium_mci_clk"; + status = "disabled"; + }; + + mmc1: mmc@28001000 { + compatible = "phytium,mci"; + reg = <0x0 0x28001000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_1200mhz>; + clock-names = "phytium_mci_clk"; + status = "disabled"; + }; + + nand0: nand@28002000 { + compatible = "phytium,nfc"; + reg = <0x0 0x28002000 0x0 0x1000>; + interrupts = ; + status = "disabled"; + }; + + ddma0: ddma@28003000 { + compatible = "phytium,ddma"; + reg = <0x0 0x28003000 0x0 0x1000>; + interrupts = ; + #dma-cells = <2>; + dma-channels = <8>; + }; + + ddma1: ddma@28004000 { + compatible = "phytium,ddma"; + reg = <0x0 0x28004000 0x0 0x1000>; + interrupts = ; + #dma-cells = <2>; + dma-channels = <8>; + }; + + qspi0: spi@28008000 { + compatible = "phytium,qspi-nor"; + reg = <0x0 0x28008000 0x0 0x1000>, + <0x0 0x0 0x0 0x0fffffff>; + reg-names = "qspi", "qspi_mm"; + clocks = <&sysclk_300mhz>; + status = "disabled"; + #address-cells = <1>; + #size-cells = <0>; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0x00>; + spi-rx-bus-width = <1>; + spi-max-frequency = <50000000>; + }; + }; + + uart0: uart@2800c000 { + compatible = "arm,pl011","arm,primecell"; + reg = <0x0 0x2800c000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_100mhz &sysclk_100mhz>; + clock-names = "uartclk", "apb_pclk"; + status = "disabled"; + }; + + uart1: uart@2800d000 { + compatible = "arm,pl011","arm,primecell"; + reg = <0x0 0x2800d000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_100mhz &sysclk_100mhz>; + clock-names = "uartclk", "apb_pclk"; + status = "disabled"; + }; + + uart2: uart@2800e000 { + compatible = "arm,pl011","arm,primecell"; + reg = <0x0 0x2800e000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_100mhz &sysclk_100mhz>; + clock-names = "uartclk", "apb_pclk"; + status = "disabled"; + }; + + uart3: uart@2800f000 { + compatible = "arm,pl011","arm,primecell"; + reg = <0x0 0x2800f000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_100mhz &sysclk_100mhz>; + clock-names = "uartclk", "apb_pclk"; + status = "disabled"; + }; + + lpc: lpc@28010000 { + compatible = "simple-mfd", "syscon"; + reg = <0x0 0x28010000 0x0 0x1000>; + reg-io-width = <4>; + + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x0 0x28010000 0x1000>; + + kcs0: kcs@24 { + compatible = "phytium,kcs-bmc"; + reg = <0x24 0x1>, <0x30 0x1>, <0x3c 0x1>; + interrupts = ; + status = "disabled"; + }; + + kcs1: kcs@28 { + compatible = "phytium,kcs-bmc"; + reg = <0x28 0x1>, <0x34 0x1>, <0x40 0x1>; + interrupts = ; + status = "disabled"; + }; + + kcs2: kcs@2c { + compatible = "phytium,kcs-bmc"; + reg = <0x2c 0x1>, <0x38 0x1>, <0x44 0x1>; + interrupts = ; + status = "disabled"; + }; + + kcs3: kcs@8c { + compatible = "phytium,kcs-bmc"; + reg = <0x8c 0x1>, <0x90 0x1>, <0x94 0x1>; + interrupts = ; + status = "disabled"; + }; + + bt: bt@48 { + compatible = "phytium,bt-bmc"; + reg = <0x48 0x20>; + interrupts = ; + status = "disabled"; + }; + }; + + /* MIO */ + + gpio0: gpio@28034000 { + compatible = "phytium,gpio"; + reg = <0x0 0x28034000 0x0 0x1000>; + interrupts = , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ; + gpio-controller; + #gpio-cells = <2>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + + porta { + compatible = "phytium,gpio-port"; + reg = <0>; + ngpios = <16>; + }; + }; + + gpio1: gpio@28035000 { + compatible = "phytium,gpio"; + reg = <0x0 0x28035000 0x0 0x1000>; + interrupts = , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ; + gpio-controller; + #gpio-cells = <2>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + + porta { + compatible = "phytium,gpio-port"; + reg = <0>; + ngpios = <16>; + }; + }; + + gpio2: gpio@28036000 { + compatible = "phytium,gpio"; + reg = <0x0 0x28036000 0x0 0x1000>; + interrupts = , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ; + gpio-controller; + #gpio-cells = <2>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + + porta { + compatible = "phytium,gpio-port"; + reg = <0>; + ngpios = <16>; + }; + }; + + gpio3: gpio@28037000 { + compatible = "phytium,gpio"; + reg = <0x0 0x28037000 0x0 0x1000>; + interrupts = ; + gpio-controller; + #gpio-cells = <2>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + + porta { + compatible = "phytium,gpio-port"; + reg = <0>; + ngpios = <16>; + }; + }; + + gpio4: gpio@28038000 { + compatible = "phytium,gpio"; + reg = <0x0 0x28038000 0x0 0x1000>; + interrupts = ; + gpio-controller; + #gpio-cells = <2>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + + porta { + compatible = "phytium,gpio-port"; + reg = <0>; + ngpios = <16>; + }; + }; + + gpio5: gpio@28039000 { + compatible = "phytium,gpio"; + reg = <0x0 0x28039000 0x0 0x1000>; + interrupts = ; + gpio-controller; + #gpio-cells = <2>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + + porta { + compatible = "phytium,gpio-port"; + reg = <0>; + ngpios = <16>; + }; + }; + + spi0: spi@2803a000 { + compatible = "phytium,spi"; + reg = <0x0 0x2803a000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + num-cs = <4>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + spi1: spi@2803b000 { + compatible = "phytium,spi"; + reg = <0x0 0x2803b000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + num-cs = <4>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + spi2: spi@2803c000 { + compatible = "phytium,spi"; + reg = <0x0 0x2803c000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + num-cs = <4>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + spi3: spi@2803d000 { + compatible = "phytium,spi"; + reg = <0x0 0x2803d000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + num-cs = <4>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + watchdog0: watchdog@28040000 { + compatible = "arm,sbsa-gwdt"; + reg = <0x0 0x28041000 0x0 0x1000>, + <0x0 0x28040000 0x0 0x1000>; + interrupts = ; + timeout-sec = <30>; + status = "disabled"; + }; + + watchdog1: watchdog@28042000 { + compatible = "arm,sbsa-gwdt"; + reg = <0x0 0x28043000 0x0 0x1000>, + <0x0 0x28042000 0x0 0x1000>; + interrupts = ; + timeout-sec = <30>; + status = "disabled"; + }; + + pwm0: pwm@2804a000 { + compatible = "phytium,pwm"; + reg = <0x0 0x2804a000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + status = "disabled"; + }; + + pwm1: pwm@2804b000 { + compatible = "phytium,pwm"; + reg = <0x0 0x2804b000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + status = "disabled"; + }; + + tacho0: tacho@28054000 { + compatible = "phytium,tacho"; + reg = <0x0 0x28054000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz >; + status = "disabled"; + }; + + tacho1: tacho@28055000 { + compatible = "phytium,tacho"; + reg = <0x0 0x28055000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz >; + status = "disabled"; + }; + + tacho2: tacho@28056000 { + compatible = "phytium,tacho"; + reg = <0x0 0x28056000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz >; + status = "disabled"; + }; + + tacho3: tacho@28057000 { + compatible = "phytium,tacho"; + reg = <0x0 0x28057000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz >; + status = "disabled"; + }; + + tacho4: tacho@28058000 { + compatible = "phytium,tacho"; + reg = <0x0 0x28058000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz >; + status = "disabled"; + }; + + tacho5: tacho@28059000 { + compatible = "phytium,tacho"; + reg = <0x0 0x28059000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz >; + status = "disabled"; + }; + + tacho6: tacho@2805a000 { + compatible = "phytium,tacho"; + reg = <0x0 0x2805a000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz >; + status = "disabled"; + }; + + tacho7: tacho@2805b000 { + compatible = "phytium,tacho"; + reg = <0x0 0x2805b000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz >; + status = "disabled"; + }; + + tacho8: tacho@2805c000 { + compatible = "phytium,tacho"; + reg = <0x0 0x2805c000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz >; + status = "disabled"; + }; + + tacho9: tacho@2805d000 { + compatible = "phytium,tacho"; + reg = <0x0 0x2805d000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz >; + status = "disabled"; + }; + + tacho10: tacho@2805e000 { + compatible = "phytium,tacho"; + reg = <0x0 0x2805e000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz >; + status = "disabled"; + }; + + tacho11: tacho@2805f000 { + compatible = "phytium,tacho"; + reg = <0x0 0x2805f000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz >; + status = "disabled"; + }; + + tacho12: tacho@28060000 { + compatible = "phytium,tacho"; + reg = <0x0 0x28060000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz >; + status = "disabled"; + }; + + tacho13: tacho@28061000 { + compatible = "phytium,tacho"; + reg = <0x0 0x28061000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz >; + status = "disabled"; + }; + + tacho14: tacho@28062000 { + compatible = "phytium,tacho"; + reg = <0x0 0x28062000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz >; + status = "disabled"; + }; + + tacho15: tacho@28063000 { + compatible = "phytium,tacho"; + reg = <0x0 0x28063000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz >; + status = "disabled"; + }; + + tacho16: tacho@28064000 { + compatible = "phytium,tacho"; + reg = <0x0 0x28064000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz >; + status = "disabled"; + }; + + tacho17: tacho@28065000 { + compatible = "phytium,tacho"; + reg = <0x0 0x28065000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz >; + status = "disabled"; + }; + + tacho18: tacho@28066000 { + compatible = "phytium,tacho"; + reg = <0x0 0x28066000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz >; + status = "disabled"; + }; + + tacho19: tacho@28067000 { + compatible = "phytium,tacho"; + reg = <0x0 0x28067000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz >; + status = "disabled"; + }; + + tacho20: tacho@28068000 { + compatible = "phytium,tacho"; + reg = <0x0 0x28068000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz >; + status = "disabled"; + }; + + tacho21: tacho@28069000 { + compatible = "phytium,tacho"; + reg = <0x0 0x28069000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz >; + status = "disabled"; + }; + + tacho22: tacho@2806a000 { + compatible = "phytium,tacho"; + reg = <0x0 0x2806a000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz >; + status = "disabled"; + }; + + tacho23: tacho@2806b000 { + compatible = "phytium,tacho"; + reg = <0x0 0x2806b000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz >; + status = "disabled"; + }; + + tacho24: tacho@2806c000 { + compatible = "phytium,tacho"; + reg = <0x0 0x2806c000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz >; + status = "disabled"; + }; + + tacho25: tacho@2806d000 { + compatible = "phytium,tacho"; + reg = <0x0 0x2806d000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz >; + status = "disabled"; + }; + + tacho26: tacho@2806e000 { + compatible = "phytium,tacho"; + reg = <0x0 0x2806e000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz >; + status = "disabled"; + }; + + tacho27: tacho@2806f000 { + compatible = "phytium,tacho"; + reg = <0x0 0x2806f000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz >; + status = "disabled"; + }; + + tacho28: tacho@28070000 { + compatible = "phytium,tacho"; + reg = <0x0 0x28070000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz >; + status = "disabled"; + }; + + tacho29: tacho@28071000 { + compatible = "phytium,tacho"; + reg = <0x0 0x28071000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz >; + status = "disabled"; + }; + + tacho30: tacho@28072000 { + compatible = "phytium,tacho"; + reg = <0x0 0x28072000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz >; + status = "disabled"; + }; + + tacho31: tacho@28073000 { + compatible = "phytium,tacho"; + reg = <0x0 0x28073000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz >; + status = "disabled"; + }; + + tacho32: tacho@28074000 { + compatible = "phytium,tacho"; + reg = <0x0 0x28074000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz >; + status = "disabled"; + }; + + tacho33: tacho@28075000 { + compatible = "phytium,tacho"; + reg = <0x0 0x28075000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz >; + status = "disabled"; + }; + + tacho34: tacho@28076000 { + compatible = "phytium,tacho"; + reg = <0x0 0x28076000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz >; + status = "disabled"; + }; + + tacho35: tacho@28077000 { + compatible = "phytium,tacho"; + reg = <0x0 0x28077000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz >; + status = "disabled"; + }; + + tacho36: tacho@28078000 { + compatible = "phytium,tacho"; + reg = <0x0 0x28078000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz >; + status = "disabled"; + }; + + tacho37: tacho@28079000 { + compatible = "phytium,tacho"; + reg = <0x0 0x28079000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz >; + status = "disabled"; + }; + + usb2_0: usb2@31800000 { /* usb_vhub0 USB2_P2 only otg mode */ + compatible = "phytium,usb2"; + reg = <0x0 0x31800000 0x0 0x80000>, + <0x0 0x31990000 0x0 0x10000>; + interrupts = ; + status = "disabled"; + }; + + usb2_1: usb2@31880000 { /* usb_vhub1 USB2_P2 */ + compatible = "phytium,usb2"; + reg = <0x0 0x31880000 0x0 0x80000>, + <0x0 0x319a0000 0x0 0x10000>; + interrupts = ; + status = "disabled"; + }; + + usb2_2: usb2@31900000 { /* usb_vhub2 USB2_P2 */ + compatible = "phytium,usb2"; + reg = <0x0 0x31900000 0x0 0x80000>, + <0x0 0x319b0000 0x0 0x10000>; + interrupts = ; + status = "disabled"; + }; + + usb2_3: usb2@32800000 { /* USB2_0 controller registers USB2_P3 */ + compatible = "phytium,usb2"; + reg = <0x0 0x32800000 0x0 0x40000>, + <0x0 0x32880000 0x0 0x40000>; /* USB2_0 UIB registers */ + interrupts = ; + status = "disabled"; + }; + + usb2_4: usb2@32840000 { /* USB2_1 controller registers USB2_P4 */ + compatible = "phytium,usb2"; + reg = <0x0 0x32840000 0x0 0x40000>, + <0x0 0x328c0000 0x0 0x40000>; /* USB2_1 UIB registers */ + interrupts = ; + status = "disabled"; + }; + + dc0: dc@32000000 { + compatible = "phytium,dc"; + reg = <0x0 0x32000000 0x0 0x8000>; + interrupts = ; + status = "disabled"; + }; + + i2s_dp0: i2s_dp0@32009000 { + compatible = "phytium,i2s"; + reg = <0x0 0x32009000 0x0 0x1000>, + <0x0 0x32008000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_600mhz>; + clock-names = "i2s_clk"; + dai-name = "phytium-i2s-dp0"; + status = "disabled"; + }; + + i2s_dp1: i2s_dp1@3200B000 { + compatible = "phytium,i2s"; + reg = <0x0 0x3200B000 0x0 0x1000>, + <0x0 0x3200A000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_600mhz>; + clock-names = "i2s_clk"; + dai-name = "phytium-i2s-dp1"; + status = "disabled"; + }; + + pmdk_dp: pmdk_dp { + compatible = "phytium,pmdk-dp"; + status = "disabled"; + }; + + mbox: mailbox@32a00000 { + compatible = "phytium,mbox"; + reg = <0x0 0x32a00000 0x0 0x1000>; + interrupts = ; + #mbox-cells = <1>; + }; + + rng0: rng@32a36000 { + compatible = "phytium,rng"; + reg = <0x0 0x32a36000 0x0 0x1000>; + status = "disabled"; + }; + + sram: sram@32a10000 { + compatible = "phytium,pe220x-sram-ns", "mmio-sram"; + reg = <0x0 0x32a10000 0x0 0x2000>; + + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x0 0x32a10000 0x2000>; + + cpu_scp_lpri: scp-shmem@0 { + compatible = "arm,scmi-shmem"; + reg = <0x1000 0x400>; + }; + + cpu_scp_hpri: scp-shmem@1 { + compatible = "arm,scmi-shmem"; + reg = <0x1400 0x400>; + }; + }; + + hwspinlock: spinlock@32b36000 { + compatible = "phytium,hwspinlock"; + reg = <0x0 0x32b36000 0x0 0x1000>; + #hwlock-cells = <1>; + nr-locks = <32>; + status = "disabled"; + }; + + pcie: pcie@40000000 { + compatible = "pci-host-ecam-generic"; + device_type = "pci"; + #address-cells = <3>; + #size-cells = <2>; + #interrupt-cells = <1>; + reg = <0x0 0x40000000 0x0 0x10000000>; + msi-parent = <&its>; + bus-range = <0x0 0xff>; + interrupt-map-mask = <0x0 0x0 0x0 0x7>; + interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0x0 GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>, + <0x0 0x0 0x0 0x2 &gic 0x0 0x0 GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>, + <0x0 0x0 0x0 0x3 &gic 0x0 0x0 GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>, + <0x0 0x0 0x0 0x4 &gic 0x0 0x0 GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>; + ranges = <0x01000000 0x00 0x00000000 0x0 0x50000000 0x0 0x00f00000>, + <0x02000000 0x00 0x58000000 0x0 0x58000000 0x0 0x28000000>, + <0x03000000 0x10 0x00000000 0x10 0x00000000 0x10 0x00000000>; + iommu-map = <0x0 &smmu 0x0 0x10000>; + status = "disabled"; + }; + + edac: edac@32b28000 { + compatible = "phytium,pe220x-edac"; + reg = <0x0 0x32b28000 0x0 0x1000>, + <0x0 0x31400000 0x0 0x1000>, + <0x0 0x31401000 0x0 0x1000>; + interrupts = , + ; + status = "disabled"; + }; + }; +}; diff --git a/target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/phytiumpi_firefly.dts b/target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/phytiumpi_firefly.dts new file mode 100644 index 000000000..d935c7d9d --- /dev/null +++ b/target/linux/phytium/files-5.10/arch/arm64/boot/dts/phytium/phytiumpi_firefly.dts @@ -0,0 +1,316 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * DTS file for Phytium Pi development board. + * + * Copyright (C) 2023, Phytium Technology Co., Ltd. + * + * Shaojun Yang + */ + +/dts-v1/; +/memreserve/ 0x80000000 0x10000; + +#include "pe2204.dtsi" + +/{ + model = "Phytium Pi Board"; + compatible = "phytium,pe2204"; + + chosen { + stdout-path = "serial1:115200n8"; + }; + aliases { + serial4 = &mio0; + serial5 = &mio11; + serial6 = &mio15; + }; + + memory@00{ + device_type = "memory"; + reg = <0x0 0x80000000 0x2 0x00000000>; + }; + + leds { + compatible = "gpio-leds"; + sysled { + label = "sysled"; + gpios = <&gpio1 5 0>; + linux,default-trigger = "none"; + }; + }; + + sound_card: sound { + compatible = "simple-audio-card"; + simple-audio-card,format = "i2s"; + simple-audio-card,name = "phytium,pe220x-i2s-audio"; + simple-audio-card,pin-switches = "mic-in"; + simple-audio-card,widgets = "Microphone","mic-in"; + simple-audio-card,routing = "MIC2","mic-in"; + simple-audio-card,cpu { + sound-dai = <&i2s0>; + }; + simple-audio-card,codec{ + sound-dai = <&codec0>; + }; + }; +}; + +&soc { + mio9: i2c@28026000 { + compatible = "phytium,i2c"; + reg = <0x0 0x28026000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + }; + + mio14: i2c@28030000 { + compatible = "phytium,i2c"; + reg = <0x0 0x28030000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + codec0: es8336@10 { + det-gpios = <&gpio2 11 0>; + sel-gpios = <&gpio2 7 0>; + #sound-dai-cells = <0>; + compatible = "everest,es8336"; + reg = <0x10>; + }; + }; + + + mio0: uart@28014000 { + compatible = "arm,pl011","arm,primecell"; + reg = <0x0 0x28014000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz &sysclk_50mhz>; + clock-names = "uartclk", "apb_pclk"; + status = "okay"; + }; + + mio1: i2c@28016000 { + compatible = "phytium,i2c"; + reg = <0x0 0x28016000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + }; + + mio8: i2c@28024000 { + compatible = "phytium,i2c"; + reg = <0x0 0x28024000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + }; + + mio11: uart@2802A000 { + compatible = "arm,pl011","arm,primecell"; + reg = <0x0 0x2802A000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz &sysclk_50mhz>; + clock-names = "uartclk", "apb_pclk"; + status = "okay"; + }; + + mio15: uart@28032000 { + compatible = "arm,pl011","arm,primecell"; + reg = <0x0 0x28032000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz &sysclk_50mhz>; + clock-names = "uartclk", "apb_pclk"; + status = "okay"; + }; +}; + +&gpio0 { + status = "okay"; +}; + +&gpio1 { + status = "okay"; +}; + +&gpio2 { + status = "okay"; +}; + +&gpio3 { + status = "okay"; +}; + +&gpio4 { + status = "okay"; +}; + +&gpio5 { + status = "okay"; +}; + +&watchdog0 { + status = "okay"; +}; + +&watchdog1 { + status = "okay"; +}; + +&pcie { + status = "okay"; +}; + +&usb3_0 { + status = "okay"; +}; + +&usb3_1 { + status = "okay"; +}; + +&usb2_0 { + dr_mode = "host"; + status = "okay"; +}; + +&usb2_1 { + dr_mode = "peripheral"; + status = "disabled"; +}; + +&usb2_2 { + dr_mode = "peripheral"; + status = "disabled"; +}; + +&usb2_3 { + dr_mode = "host"; + status = "okay"; +}; + +&usb2_4 { + dr_mode = "host"; + status = "okay"; +}; + +&macb0 { + phy-mode = "sgmii"; + use-mii; + status = "okay"; +}; + +&macb1 { + phy-mode = "sgmii"; + use-mii; + status = "okay"; +}; + +&dc0 { + pipe_mask = /bits/ 8 <0x1>; + edp_mask = /bits/ 8 <0x0>; + status = "okay"; +}; + +&i2s0 { + #sound-dai-cells = <0>; + dai-name = "phytium-i2s-lsd"; + status = "okay"; +}; + +&i2s_dp0 { + dai-name = "phytium-i2s-dp0"; + status = "okay"; +}; + +&qspi0 { + status = "okay"; + + flash@0 { + status = "okay"; + }; +}; + +&spi0 { + global-cs = <1>; + status = "okay"; + + spidev0: spidev@0 { + compatible = "spidev"; + reg = <0>; + spi-max-frequency = <50000000>; + status = "disabled"; + }; +}; + +&mmc0 { + bus-width = <0x00000004>; + max-frequency = <50000000>; + cap-sdio-irq; + cap-sd-highspeed; + no-mmc; + status = "okay"; +}; + +&mmc1 { + bus-width = <0x00000004>; + max-frequency = <25000000>; + cap-sdio-irq; + cap-sd-highspeed; + no-mmc; + no-sd; + non-removable; + status = "okay"; +}; + +&pwm0 { + phytium,db = <0 0 100 1000 1000 0>; + status = "okay"; +}; + +&pwm1 { + phytium,db = <0 0 100 1000 1000 0>; + status = "okay"; +}; + +&uart0 { + status = "okay"; +}; + +&uart1 { + status = "okay"; +}; + +&uart2 { + status = "okay"; +}; + +&uart3 { + status = "okay"; +}; + +&can0 { + status = "okay"; +}; + +&can1 { + status = "okay"; +}; + +&pmdk_dp { + num-dp = <1>; + dp-mask = /bits/ 8 <0x1>; + status = "okay"; +}; + +&rng0 { + status = "okay"; +}; diff --git a/target/linux/phytium/files-5.10/arch/arm64/configs/phytium_defconfig b/target/linux/phytium/files-5.10/arch/arm64/configs/phytium_defconfig new file mode 100644 index 000000000..d1d1c81d6 --- /dev/null +++ b/target/linux/phytium/files-5.10/arch/arm64/configs/phytium_defconfig @@ -0,0 +1,770 @@ +CONFIG_LOCALVERSION="-phytium-embeded-v2.0" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y +CONFIG_AUDIT=y +CONFIG_NO_HZ_IDLE=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_PREEMPT=y +CONFIG_IRQ_TIME_ACCOUNTING=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_BSD_PROCESS_ACCT_V3=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_MEMCG=y +CONFIG_BLK_CGROUP=y +CONFIG_CGROUP_PIDS=y +CONFIG_CGROUP_HUGETLB=y +CONFIG_CPUSETS=y +CONFIG_CGROUP_DEVICE=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_CGROUP_PERF=y +CONFIG_USER_NS=y +CONFIG_SCHED_AUTOGROUP=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_KALLSYMS_ALL=y +# CONFIG_COMPAT_BRK is not set +CONFIG_PROFILING=y +CONFIG_ARCH_PHYTIUM=y +CONFIG_ARM64_VA_BITS_48=y +CONFIG_SCHED_MC=y +CONFIG_SCHED_SMT=y +CONFIG_KEXEC=y +CONFIG_KEXEC_FILE=y +CONFIG_CRASH_DUMP=y +CONFIG_XEN=y +CONFIG_COMPAT=y +CONFIG_RANDOMIZE_BASE=y +CONFIG_HIBERNATION=y +CONFIG_WQ_POWER_EFFICIENT_DEFAULT=y +CONFIG_ENERGY_MODEL=y +CONFIG_ARM_CPUIDLE=y +CONFIG_ARM_PSCI_CPUIDLE=y +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=m +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=m +CONFIG_CPUFREQ_DT=y +CONFIG_ACPI_CPPC_CPUFREQ=m +CONFIG_ARM_SCPI_CPUFREQ=y +CONFIG_ARM_SCMI_CPUFREQ=y +CONFIG_ARM_SCMI_PROTOCOL=y +CONFIG_ARM_SCMI_TRANSPORT_FORCE_POLLING=y +CONFIG_ARM_SCPI_PROTOCOL=y +CONFIG_EFI_CAPSULE_LOADER=y +CONFIG_ACPI=y +CONFIG_ACPI_APEI=y +CONFIG_ACPI_APEI_GHES=y +CONFIG_ACPI_APEI_MEMORY_FAILURE=y +CONFIG_ACPI_APEI_EINJ=y +CONFIG_VIRTUALIZATION=y +CONFIG_KVM=y +CONFIG_ARM64_CRYPTO=y +CONFIG_CRYPTO_SHA1_ARM64_CE=y +CONFIG_CRYPTO_SHA2_ARM64_CE=y +CONFIG_CRYPTO_SHA512_ARM64_CE=m +CONFIG_CRYPTO_SHA3_ARM64=m +CONFIG_CRYPTO_SM3_ARM64_CE=m +CONFIG_CRYPTO_GHASH_ARM64_CE=y +CONFIG_CRYPTO_CRCT10DIF_ARM64_CE=m +CONFIG_CRYPTO_AES_ARM64_CE_CCM=y +CONFIG_CRYPTO_AES_ARM64_CE_BLK=y +CONFIG_CRYPTO_CHACHA20_NEON=m +CONFIG_CRYPTO_AES_ARM64_BS=m +CONFIG_KPROBES=y +CONFIG_JUMP_LABEL=y +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_BLK_DEV_INTEGRITY=y +CONFIG_PARTITION_ADVANCED=y +CONFIG_CMDLINE_PARTITION=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_KSM=y +CONFIG_MEMORY_FAILURE=y +CONFIG_TRANSPARENT_HUGEPAGE=y +CONFIG_CMA=y +CONFIG_CMA_AREAS=19 +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IPV6=m +CONFIG_NETFILTER=y +CONFIG_NF_CONNTRACK=m +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NETFILTER_XT_TARGET_CHECKSUM=m +CONFIG_NETFILTER_XT_TARGET_LOG=m +CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=m +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=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_MANGLE=m +CONFIG_IP6_NF_IPTABLES=m +CONFIG_IP6_NF_FILTER=m +CONFIG_IP6_NF_TARGET_REJECT=m +CONFIG_IP6_NF_MANGLE=m +CONFIG_IP6_NF_NAT=m +CONFIG_IP6_NF_TARGET_MASQUERADE=m +CONFIG_BRIDGE=m +CONFIG_BRIDGE_VLAN_FILTERING=y +CONFIG_NET_DSA=m +CONFIG_NET_DSA_TAG_OCELOT=m +CONFIG_VLAN_8021Q=m +CONFIG_VLAN_8021Q_GVRP=y +CONFIG_VLAN_8021Q_MVRP=y +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_CBS=m +CONFIG_NET_SCH_ETF=m +CONFIG_NET_SCH_TAPRIO=m +CONFIG_NET_SCH_MQPRIO=m +CONFIG_NET_SCH_INGRESS=m +CONFIG_NET_CLS_BASIC=m +CONFIG_NET_CLS_FLOWER=m +CONFIG_NET_CLS_ACT=y +CONFIG_NET_ACT_GACT=m +CONFIG_NET_ACT_MIRRED=m +CONFIG_NET_ACT_GATE=m +CONFIG_QRTR=m +CONFIG_QRTR_SMD=m +CONFIG_QRTR_TUN=m +CONFIG_BPF_JIT=y +CONFIG_CAN=m +CONFIG_CAN_FLEXCAN=m +CONFIG_CAN_PHYTIUM=m +CONFIG_CAN_PHYTIUM_PLATFORM=m +CONFIG_CAN_PHYTIUM_PCI=m +CONFIG_BT=y +CONFIG_BT_RFCOMM=m +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=m +CONFIG_BT_HIDP=m +# CONFIG_BT_LE is not set +CONFIG_BT_LEDS=y +# CONFIG_BT_DEBUGFS is not set +CONFIG_BT_HCIBTUSB=m +CONFIG_BT_HCIUART=y +CONFIG_BT_HCIUART_3WIRE=y +CONFIG_CFG80211=y +CONFIG_CFG80211_WEXT=y +CONFIG_MAC80211=y +CONFIG_MAC80211_LEDS=y +CONFIG_RFKILL=y +CONFIG_NET_9P=y +CONFIG_NET_9P_VIRTIO=y +CONFIG_NFC=m +CONFIG_NFC_NCI=m +CONFIG_NFC_S3FWRN5_I2C=m +CONFIG_PCI=y +CONFIG_PCIEPORTBUS=y +CONFIG_HOTPLUG_PCI_PCIE=y +CONFIG_PCI_IOV=y +CONFIG_PCI_PASID=y +CONFIG_HOTPLUG_PCI=y +CONFIG_HOTPLUG_PCI_ACPI=y +CONFIG_PCI_HOST_GENERIC=y +CONFIG_PCI_XGENE=y +CONFIG_PCIE_ALTERA=y +CONFIG_PCIE_ALTERA_MSI=y +CONFIG_PCI_HOST_THUNDER_PEM=y +CONFIG_PCI_HOST_THUNDER_ECAM=y +CONFIG_PCIE_LAYERSCAPE_GEN4=y +CONFIG_PCI_ENDPOINT=y +CONFIG_PCI_ENDPOINT_CONFIGFS=y +CONFIG_PCI_EPF_TEST=m +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_FW_LOADER_USER_HELPER=y +CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y +CONFIG_BRCMSTB_GISB_ARB=y +CONFIG_SIMPLE_PM_BUS=y +CONFIG_VEXPRESS_CONFIG=y +CONFIG_MTD=y +CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_BLOCK=y +CONFIG_MTD_CFI=y +CONFIG_MTD_CFI_ADV_OPTIONS=y +CONFIG_MTD_CFI_INTELEXT=y +CONFIG_MTD_CFI_AMDSTD=y +CONFIG_MTD_CFI_STAA=y +CONFIG_MTD_PHYSMAP=y +CONFIG_MTD_PHYSMAP_OF=y +CONFIG_MTD_DATAFLASH=y +CONFIG_MTD_SST25L=y +CONFIG_MTD_RAW_NAND=y +CONFIG_MTD_NAND_DENALI_DT=y +CONFIG_MTD_NAND_PHYTIUM_PLAT=m +CONFIG_MTD_SPI_NOR=y +CONFIG_SPI_PHYTIUM_QUADSPI=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_NBD=m +CONFIG_BLK_DEV_RAM=y +CONFIG_VIRTIO_BLK=y +CONFIG_BLK_DEV_NVME=y +CONFIG_NVME_MULTIPATH=y +CONFIG_NVME_HWMON=y +CONFIG_NVME_FC=y +CONFIG_NVME_TCP=y +CONFIG_NVME_TARGET=y +CONFIG_NVME_TARGET_PASSTHRU=y +CONFIG_NVME_TARGET_LOOP=y +CONFIG_NVME_TARGET_FC=y +CONFIG_NVME_TARGET_FCLOOP=y +CONFIG_NVME_TARGET_TCP=y +CONFIG_SRAM=y +CONFIG_PCI_ENDPOINT_TEST=m +CONFIG_EEPROM_AT24=m +CONFIG_EEPROM_AT25=m +CONFIG_UACCE=m +# CONFIG_SCSI_PROC_FS is not set +CONFIG_BLK_DEV_SD=y +CONFIG_SCSI_SAS_LIBSAS=y +CONFIG_SCSI_SAS_ATA=y +CONFIG_MEGARAID_SAS=y +CONFIG_SCSI_MPT3SAS=m +CONFIG_SCSI_UFSHCD=y +CONFIG_SCSI_UFSHCD_PLATFORM=y +CONFIG_ATA=y +CONFIG_SATA_AHCI=y +CONFIG_SATA_AHCI_PLATFORM=y +CONFIG_AHCI_CEVA=y +CONFIG_AHCI_XGENE=y +CONFIG_AHCI_QORIQ=y +CONFIG_SATA_SIL24=y +CONFIG_PATA_PLATFORM=y +CONFIG_PATA_OF_PLATFORM=y +CONFIG_MD=y +CONFIG_BLK_DEV_MD=m +CONFIG_BLK_DEV_DM=m +CONFIG_DM_MIRROR=m +CONFIG_DM_ZERO=m +CONFIG_NETDEVICES=y +CONFIG_MACVLAN=m +CONFIG_MACVTAP=m +CONFIG_TUN=y +CONFIG_VETH=m +CONFIG_VIRTIO_NET=y +CONFIG_AMD_XGBE=y +CONFIG_ATL1C=m +CONFIG_BCMGENET=m +CONFIG_BNX2X=m +CONFIG_MACB=y +CONFIG_MACB_PCI=y +CONFIG_THUNDER_NIC_PF=y +# CONFIG_NET_VENDOR_HISILICON is not set +# CONFIG_NET_VENDOR_HUAWEI is not set +CONFIG_E1000=m +CONFIG_E1000E=m +CONFIG_IGB=m +CONFIG_IGBVF=m +CONFIG_IXGB=m +CONFIG_IXGBE=m +CONFIG_IXGBEVF=m +CONFIG_I40E=m +CONFIG_I40EVF=m +CONFIG_MVMDIO=y +CONFIG_SKY2=y +CONFIG_MLX4_EN=m +CONFIG_MLX5_CORE=m +CONFIG_MLX5_CORE_EN=y +CONFIG_QCOM_EMAC=m +CONFIG_RMNET=m +CONFIG_SMC91X=y +CONFIG_SMSC911X=y +CONFIG_STMMAC_ETH=y +CONFIG_AQUANTIA_PHY=y +CONFIG_BROADCOM_PHY=m +CONFIG_MARVELL_PHY=m +CONFIG_MARVELL_10G_PHY=m +CONFIG_MICREL_PHY=y +CONFIG_MICROSEMI_PHY=y +CONFIG_AT803X_PHY=y +CONFIG_REALTEK_PHY=m +CONFIG_ROCKCHIP_PHY=y +CONFIG_VITESSE_PHY=y +CONFIG_MOTORCOMM_PHY=y +CONFIG_MDIO_BITBANG=y +CONFIG_MDIO_BUS_MUX_MULTIPLEXER=y +CONFIG_MDIO_BUS_MUX_MMIOREG=y +CONFIG_PPP=y +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_ASYNC=y +CONFIG_PPP_SYNC_TTY=y +CONFIG_USB_PEGASUS=m +CONFIG_USB_RTL8150=m +CONFIG_USB_RTL8152=m +CONFIG_USB_LAN78XX=m +CONFIG_USB_USBNET=y +CONFIG_USB_NET_AX8817X=m +CONFIG_USB_NET_AX88179_178A=m +CONFIG_USB_NET_CDCETHER=m +CONFIG_USB_NET_CDC_NCM=m +CONFIG_USB_NET_DM9601=m +CONFIG_USB_NET_SR9800=m +CONFIG_USB_NET_SMSC75XX=m +CONFIG_USB_NET_SMSC95XX=m +CONFIG_USB_NET_NET1080=m +CONFIG_USB_NET_PLUSB=m +CONFIG_USB_NET_MCS7830=m +CONFIG_USB_NET_CDC_SUBSET=m +CONFIG_USB_NET_ZAURUS=m +CONFIG_USB_NET_QMI_WWAN=y +CONFIG_ATH10K=m +CONFIG_ATH10K_PCI=m +CONFIG_BRCMFMAC=m +CONFIG_MWIFIEX=m +CONFIG_MWIFIEX_PCIE=m +CONFIG_RTL_CARDS=m +CONFIG_WL18XX=m +CONFIG_WLCORE_SDIO=m +CONFIG_RTL8821CS=m +CONFIG_INPUT_EVDEV=y +CONFIG_KEYBOARD_ADC=m +CONFIG_KEYBOARD_GPIO=y +CONFIG_KEYBOARD_CROS_EC=y +CONFIG_KEYBOARD_PHYTIUM=m +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_ATMEL_MXT=m +CONFIG_INPUT_MISC=y +# CONFIG_SERIO_SERPORT is not set +CONFIG_SERIO_AMBAKMI=y +CONFIG_LEGACY_PTY_COUNT=16 +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_EXTENDED=y +CONFIG_SERIAL_8250_SHARE_IRQ=y +CONFIG_SERIAL_8250_DW=y +CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SERIAL_AMBA_PL011=y +CONFIG_SERIAL_AMBA_PL011_CONSOLE=y +CONFIG_SERIAL_PHYTIUM_PCI=m +CONFIG_SERIAL_XILINX_PS_UART=y +CONFIG_SERIAL_XILINX_PS_UART_CONSOLE=y +CONFIG_SERIAL_FSL_LPUART=y +CONFIG_SERIAL_FSL_LPUART_CONSOLE=y +CONFIG_SERIAL_FSL_LINFLEXUART=y +CONFIG_SERIAL_FSL_LINFLEXUART_CONSOLE=y +CONFIG_SERIAL_DEV_BUS=y +CONFIG_VIRTIO_CONSOLE=y +CONFIG_IPMI_HANDLER=m +CONFIG_IPMI_DEVICE_INTERFACE=m +CONFIG_IPMI_SI=m +CONFIG_HW_RANDOM=y +# CONFIG_HW_RANDOM_HISI_V2 is not set +CONFIG_HW_RANDOM_PHYTIUM=y +CONFIG_TCG_TPM=y +CONFIG_TCG_TIS_I2C_INFINEON=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_MUX=y +CONFIG_I2C_MUX_PCA954x=y +CONFIG_I2C_DESIGNWARE_PLATFORM=y +CONFIG_I2C_GPIO=m +CONFIG_I2C_PHYTIUM_PCI=y +CONFIG_I2C_PHYTIUM_PLATFORM=y +CONFIG_I3C=y +CONFIG_CDNS_I3C_MASTER=y +CONFIG_SPI=y +CONFIG_SPI_BITBANG=m +CONFIG_SPI_PHYTIUM_PLAT=y +CONFIG_SPI_PHYTIUM_PCI=y +CONFIG_SPI_PHYTIUM_QSPI=y +CONFIG_SPI_SPIDEV=y +CONFIG_SPMI=y +CONFIG_PINCTRL=y +CONFIG_PINCTRL_SINGLE=y +CONFIG_PINCTRL_MAX77620=y +CONFIG_GPIO_ALTERA=m +CONFIG_GPIO_DWAPB=y +CONFIG_GPIO_MB86S7X=y +CONFIG_GPIO_PL061=y +CONFIG_GPIO_WCD934X=m +CONFIG_GPIO_XGENE=y +CONFIG_GPIO_MAX732X=y +CONFIG_GPIO_PCA953X=y +CONFIG_GPIO_PCA953X_IRQ=y +CONFIG_GPIO_BD9571MWV=m +CONFIG_GPIO_MAX77620=y +CONFIG_W1=m +CONFIG_W1_MASTER_PHYTIUM=m +CONFIG_W1_SLAVE_THERM=m +CONFIG_POWER_RESET_BRCMSTB=y +CONFIG_POWER_RESET_XGENE=y +CONFIG_POWER_RESET_SYSCON=y +CONFIG_SYSCON_REBOOT_MODE=y +CONFIG_BATTERY_SBS=m +CONFIG_BATTERY_BQ27XXX=y +CONFIG_SENSORS_ARM_SCMI=y +CONFIG_SENSORS_ARM_SCPI=y +CONFIG_SENSORS_LM90=m +CONFIG_SENSORS_PWM_FAN=m +CONFIG_SENSORS_INA2XX=m +CONFIG_SENSORS_INA3221=m +CONFIG_SENSORS_PHYTIUM=m +CONFIG_THERMAL_GOV_POWER_ALLOCATOR=y +CONFIG_CPU_THERMAL=y +CONFIG_THERMAL_EMULATION=y +CONFIG_WATCHDOG=y +CONFIG_ARM_SP805_WATCHDOG=y +CONFIG_ARM_SBSA_WATCHDOG=y +CONFIG_DW_WATCHDOG=y +CONFIG_ARM_SMC_WATCHDOG=y +CONFIG_MFD_BD9571MWV=y +CONFIG_MFD_AXP20X_I2C=y +CONFIG_MFD_HI6421_PMIC=y +CONFIG_MFD_MAX77620=y +CONFIG_MFD_PHYTIUM_I2S_LSD=y +CONFIG_MFD_PHYTIUM_I2S_MMD=y +CONFIG_MFD_RK808=y +CONFIG_MFD_SEC_CORE=y +CONFIG_MFD_ROHM_BD718XX=y +CONFIG_MFD_WCD934X=m +CONFIG_REGULATOR=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +CONFIG_REGULATOR_AXP20X=y +CONFIG_REGULATOR_BD718XX=y +CONFIG_REGULATOR_BD9571MWV=y +CONFIG_REGULATOR_FAN53555=y +CONFIG_REGULATOR_GPIO=y +CONFIG_REGULATOR_HI6421V530=y +CONFIG_REGULATOR_MAX77620=y +CONFIG_REGULATOR_MAX8973=y +CONFIG_REGULATOR_PCA9450=y +CONFIG_REGULATOR_PFUZE100=y +CONFIG_REGULATOR_PWM=y +CONFIG_REGULATOR_QCOM_SPMI=y +CONFIG_REGULATOR_RK808=y +CONFIG_REGULATOR_S2MPS11=y +CONFIG_REGULATOR_VCTRL=m +CONFIG_RC_CORE=m +CONFIG_RC_DECODERS=y +CONFIG_RC_DEVICES=y +CONFIG_MEDIA_SUPPORT=m +CONFIG_MEDIA_CAMERA_SUPPORT=y +CONFIG_MEDIA_ANALOG_TV_SUPPORT=y +CONFIG_MEDIA_DIGITAL_TV_SUPPORT=y +CONFIG_MEDIA_SDR_SUPPORT=y +CONFIG_MEDIA_PLATFORM_SUPPORT=y +# CONFIG_DVB_NET is not set +CONFIG_MEDIA_USB_SUPPORT=y +CONFIG_USB_VIDEO_CLASS=m +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_V4L_MEM2MEM_DRIVERS=y +CONFIG_SDR_PLATFORM_DRIVERS=y +CONFIG_VIDEO_IMX219=m +CONFIG_VIDEO_OV5645=m +CONFIG_DRM=y +CONFIG_DRM_I2C_CH7006=m +CONFIG_DRM_I2C_SIL164=m +CONFIG_DRM_I2C_NXP_TDA998X=m +CONFIG_DRM_MALI_DISPLAY=m +CONFIG_DRM_RCAR_DW_HDMI=m +CONFIG_DRM_RCAR_LVDS=m +CONFIG_DRM_PANEL_LVDS=m +CONFIG_DRM_PANEL_SIMPLE=m +CONFIG_DRM_PANEL_RAYDIUM_RM67191=m +CONFIG_DRM_PANEL_SITRONIX_ST7703=m +CONFIG_DRM_PANEL_TRULY_NT35597_WQXGA=m +CONFIG_DRM_DISPLAY_CONNECTOR=m +CONFIG_DRM_LONTIUM_LT9611=m +CONFIG_DRM_NWL_MIPI_DSI=m +CONFIG_DRM_SII902X=m +CONFIG_DRM_SIMPLE_BRIDGE=m +CONFIG_DRM_THINE_THC63LVD1024=m +CONFIG_DRM_TI_SN65DSI86=m +CONFIG_DRM_I2C_ADV7511=m +CONFIG_DRM_I2C_ADV7511_AUDIO=y +CONFIG_DRM_DW_HDMI_AHB_AUDIO=m +CONFIG_DRM_DW_HDMI_I2S_AUDIO=m +CONFIG_DRM_DW_HDMI_CEC=m +CONFIG_DRM_PHYTIUM=y +CONFIG_DRM_LEGACY=y +CONFIG_FB_MODE_HELPERS=y +CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_BACKLIGHT_PWM=m +CONFIG_BACKLIGHT_LP855X=m +CONFIG_LOGO=y +# CONFIG_LOGO_LINUX_MONO is not set +# CONFIG_LOGO_LINUX_VGA16 is not set +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_HDA_PHYTIUM=y +CONFIG_SND_HDA_HWDEP=y +CONFIG_SND_HDA_INPUT_BEEP=y +CONFIG_SND_HDA_PATCH_LOADER=y +CONFIG_SND_HDA_CODEC_REALTEK=y +CONFIG_SND_HDA_CODEC_HDMI=y +CONFIG_SND_SOC=y +CONFIG_SND_SOC_FSL_SAI=m +CONFIG_SND_SOC_PHYTIUM_I2S=y +CONFIG_SND_PMDK_ES8388=y +CONFIG_SND_PMDK_ES8336=y +CONFIG_SND_PMDK_DP=y +CONFIG_SND_SOC_AK4613=m +CONFIG_SND_SOC_CROS_EC_CODEC=m +CONFIG_SND_SOC_DMIC=m +CONFIG_SND_SOC_ES7134=m +CONFIG_SND_SOC_ES7241=m +CONFIG_SND_SOC_MAX98357A=m +CONFIG_SND_SOC_MAX98927=m +CONFIG_SND_SOC_PCM3168A_I2C=m +CONFIG_SND_SOC_SIMPLE_AMPLIFIER=m +CONFIG_SND_SOC_SPDIF=m +CONFIG_SND_SOC_TAS571X=m +CONFIG_SND_SOC_WCD934X=m +CONFIG_SND_SOC_WM8904=m +CONFIG_SND_SOC_WSA881X=m +CONFIG_SND_SIMPLE_CARD=y +CONFIG_SND_AUDIO_GRAPH_CARD=y +CONFIG_I2C_HID=m +CONFIG_USB_CONN_GPIO=y +CONFIG_USB=y +CONFIG_USB_OTG=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_ACM=y +CONFIG_USB_STORAGE=y +CONFIG_USB_MUSB_HDRC=y +CONFIG_USB_DWC3=y +CONFIG_USB_DWC2=y +CONFIG_USB_CHIPIDEA=y +CONFIG_USB_CHIPIDEA_UDC=y +CONFIG_USB_CHIPIDEA_HOST=y +CONFIG_USB_ISP1760=y +CONFIG_USB_PHYTIUM=y +CONFIG_USB_SERIAL=y +CONFIG_USB_SERIAL_CH341=y +CONFIG_USB_SERIAL_FTDI_SIO=m +CONFIG_USB_SERIAL_OPTION=y +CONFIG_USB_HSIC_USB3503=y +CONFIG_NOP_USB_XCEIV=y +CONFIG_USB_ULPI=y +CONFIG_USB_GADGET=y +CONFIG_USB_SNP_UDC_PLAT=y +CONFIG_USB_BDC_UDC=y +CONFIG_USB_CONFIGFS=m +CONFIG_USB_CONFIGFS_SERIAL=y +CONFIG_USB_CONFIGFS_ACM=y +CONFIG_USB_CONFIGFS_OBEX=y +CONFIG_USB_CONFIGFS_NCM=y +CONFIG_USB_CONFIGFS_ECM=y +CONFIG_USB_CONFIGFS_ECM_SUBSET=y +CONFIG_USB_CONFIGFS_RNDIS=y +CONFIG_USB_CONFIGFS_EEM=y +CONFIG_USB_CONFIGFS_MASS_STORAGE=y +CONFIG_USB_CONFIGFS_F_FS=y +CONFIG_TYPEC=m +CONFIG_TYPEC_TCPM=m +CONFIG_TYPEC_FUSB302=m +CONFIG_TYPEC_HD3SS3220=m +CONFIG_MMC=y +CONFIG_MMC_BLOCK_MINORS=32 +CONFIG_MMC_ARMMMCI=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_ACPI=y +CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_MMC_SDHCI_OF_ARASAN=y +CONFIG_MMC_SDHCI_CADENCE=y +CONFIG_MMC_SDHCI_F_SDH30=y +CONFIG_MMC_SPI=y +CONFIG_MMC_DW=y +CONFIG_MMC_DW_EXYNOS=y +CONFIG_MMC_DW_HI3798CV200=y +CONFIG_MMC_DW_K3=y +CONFIG_MMC_SDHCI_XENON=y +CONFIG_MMC_SDHCI_AM654=y +# CONFIG_MMC_PHYTIUM_MCI_PCI is not set +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_PWM=y +CONFIG_LEDS_SYSCON=y +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_LEDS_TRIGGER_DISK=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +CONFIG_LEDS_TRIGGER_CPU=y +CONFIG_LEDS_TRIGGER_DEFAULT_ON=y +CONFIG_LEDS_TRIGGER_PANIC=y +CONFIG_EDAC=y +CONFIG_EDAC_GHES=y +CONFIG_EDAC_PHYTIUM=m +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_DS1307=m +CONFIG_RTC_DRV_MAX77686=y +CONFIG_RTC_DRV_RK808=m +CONFIG_RTC_DRV_PCF85363=m +CONFIG_RTC_DRV_RX8581=m +CONFIG_RTC_DRV_RV8803=m +CONFIG_RTC_DRV_S5M=y +CONFIG_RTC_DRV_SD3068=m +CONFIG_RTC_DRV_DS3232=y +CONFIG_RTC_DRV_PCF2127=m +CONFIG_RTC_DRV_EFI=y +CONFIG_RTC_DRV_CROS_EC=y +CONFIG_RTC_DRV_PL031=y +CONFIG_DMADEVICES=y +CONFIG_BCM_SBA_RAID=m +CONFIG_FSL_EDMA=y +CONFIG_MV_XOR_V2=y +CONFIG_PL330_DMA=y +CONFIG_PHYTIUM_DDMA=y +CONFIG_UIO=m +CONFIG_UIO_CIF=m +CONFIG_UIO_PDRV_GENIRQ=m +CONFIG_UIO_DMEM_GENIRQ=m +CONFIG_UIO_AEC=m +CONFIG_UIO_SERCOS3=m +CONFIG_UIO_PCI_GENERIC=m +CONFIG_UIO_NETX=m +CONFIG_UIO_PRUSS=m +CONFIG_UIO_MF624=m +CONFIG_VFIO=y +CONFIG_VFIO_PCI=y +CONFIG_VIRTIO_PCI=y +CONFIG_VIRTIO_BALLOON=y +CONFIG_VIRTIO_MMIO=y +CONFIG_XEN_GNTDEV=y +CONFIG_XEN_GRANT_DEV_ALLOC=y +CONFIG_STAGING=y +CONFIG_CHROME_PLATFORMS=y +CONFIG_CROS_EC=y +CONFIG_CROS_EC_I2C=y +CONFIG_CROS_EC_SPI=y +CONFIG_CROS_EC_CHARDEV=m +CONFIG_COMMON_CLK_RK808=y +CONFIG_COMMON_CLK_SCPI=y +CONFIG_COMMON_CLK_CS2000_CP=y +CONFIG_COMMON_CLK_S2MPS11=y +CONFIG_CLK_QORIQ=y +CONFIG_COMMON_CLK_XGENE=y +CONFIG_COMMON_CLK_PWM=y +CONFIG_COMMON_CLK_VC5=y +CONFIG_COMMON_CLK_BD718XX=m +CONFIG_HWSPINLOCK=y +CONFIG_HWSPINLOCK_PHYTIUM=y +CONFIG_ARM_MHU=y +CONFIG_PLATFORM_MHU=y +CONFIG_PHYTIUM_MBOX=y +CONFIG_ARM_SMMU=y +CONFIG_ARM_SMMU_V3=y +CONFIG_REMOTEPROC=y +CONFIG_REMOTEPROC_CDEV=y +CONFIG_HOMO_REMOTEPROC=y +CONFIG_RPMSG_CHAR=m +CONFIG_RPMSG_QCOM_GLINK_RPM=y +CONFIG_SOUNDWIRE=m +CONFIG_SOUNDWIRE_QCOM=m +CONFIG_SOC_BRCMSTB=y +CONFIG_SOC_TI=y +CONFIG_DEVFREQ_GOV_PERFORMANCE=y +CONFIG_DEVFREQ_GOV_POWERSAVE=y +CONFIG_EXTCON_PTN5150=m +CONFIG_EXTCON_USB_GPIO=y +CONFIG_EXTCON_USBC_CROS_EC=y +CONFIG_MEMORY=y +CONFIG_IIO=y +CONFIG_MAX9611=m +CONFIG_QCOM_SPMI_ADC5=m +CONFIG_PHYTIUM_ADC=m +CONFIG_IIO_CROS_EC_SENSORS_CORE=m +CONFIG_IIO_CROS_EC_SENSORS=m +CONFIG_IIO_CROS_EC_LIGHT_PROX=m +CONFIG_SENSORS_ISL29018=m +CONFIG_IIO_CROS_EC_BARO=m +CONFIG_MPL3115=m +CONFIG_PWM=y +CONFIG_PWM_CROS_EC=m +CONFIG_PWM_PHYTIUM=m +CONFIG_PHYTIUM_IXIC=y +CONFIG_PHY_XGENE=y +CONFIG_PHY_FSL_IMX8MQ_USB=y +CONFIG_PHY_MIXEL_MIPI_DPHY=m +CONFIG_PHY_QCOM_USB_HS=y +CONFIG_PHY_SAMSUNG_USB2=y +CONFIG_ARM_SMMU_V3_PMU=m +CONFIG_FPGA=y +CONFIG_FPGA_BRIDGE=m +CONFIG_ALTERA_FREEZE_BRIDGE=m +CONFIG_FPGA_REGION=m +CONFIG_OF_FPGA_REGION=m +CONFIG_MUX_MMIO=y +CONFIG_SLIM_QCOM_CTRL=m +CONFIG_INTERCONNECT=y +CONFIG_EXT2_FS=y +CONFIG_EXT3_FS=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_BTRFS_FS=m +CONFIG_BTRFS_FS_POSIX_ACL=y +CONFIG_FANOTIFY=y +CONFIG_FANOTIFY_ACCESS_PERMISSIONS=y +CONFIG_QUOTA=y +CONFIG_AUTOFS4_FS=y +CONFIG_FUSE_FS=m +CONFIG_CUSE=m +CONFIG_OVERLAY_FS=m +CONFIG_ISO9660_FS=y +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_HUGETLBFS=y +CONFIG_EFIVAR_FS=y +CONFIG_SQUASHFS=y +CONFIG_SQUASHFS_XATTR=y +CONFIG_SQUASHFS_LZ4=y +CONFIG_SQUASHFS_LZO=y +CONFIG_SQUASHFS_XZ=y +CONFIG_SQUASHFS_ZSTD=y +CONFIG_SQUASHFS_4K_DEVBLK_SIZE=y +CONFIG_SQUASHFS_EMBEDDED=y +CONFIG_NFS_FS=y +CONFIG_NFS_V4=y +CONFIG_NFS_V4_1=y +CONFIG_NFS_V4_2=y +CONFIG_ROOT_NFS=y +CONFIG_NFSD=y +CONFIG_NFSD_V3_ACL=y +CONFIG_NFSD_V4=y +CONFIG_NFSD_BLOCKLAYOUT=y +CONFIG_NFSD_SCSILAYOUT=y +CONFIG_NFSD_FLEXFILELAYOUT=y +CONFIG_NFSD_V4_2_INTER_SSC=y +CONFIG_NFSD_V4_SECURITY_LABEL=y +CONFIG_9P_FS=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ISO8859_1=y +CONFIG_SECURITY=y +CONFIG_CRYPTO_USER=y +CONFIG_CRYPTO_ECHAINIV=y +CONFIG_CRYPTO_ANSI_CPRNG=y +CONFIG_CRYPTO_DRBG_HASH=y +CONFIG_CRYPTO_USER_API_HASH=y +CONFIG_CRYPTO_USER_API_SKCIPHER=y +CONFIG_CRYPTO_USER_API_RNG=m +CONFIG_CRYPTO_USER_API_AEAD=y +CONFIG_CRYPTO_DEV_CCREE=m +CONFIG_CRYPTO_DEV_HISI_SEC2=m +CONFIG_CRYPTO_DEV_HISI_ZIP=m +CONFIG_CRYPTO_DEV_HISI_HPRE=m +CONFIG_CRYPTO_DEV_AMLOGIC_GXL=m +CONFIG_INDIRECT_PIO=y +CONFIG_DMA_CMA=y +CONFIG_DMA_PERNUMA_CMA=y +CONFIG_CMA_SIZE_MBYTES=32 +CONFIG_PRINTK_TIME=y +CONFIG_DEBUG_INFO=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_FS=y +CONFIG_DEBUG_KERNEL=y +# CONFIG_SCHED_DEBUG is not set +# CONFIG_DEBUG_PREEMPT is not set +# CONFIG_FTRACE is not set +CONFIG_MEMTEST=y diff --git a/target/linux/phytium/files-5.10/arch/arm64/configs/phytium_optee.config b/target/linux/phytium/files-5.10/arch/arm64/configs/phytium_optee.config new file mode 100644 index 000000000..07554cf84 --- /dev/null +++ b/target/linux/phytium/files-5.10/arch/arm64/configs/phytium_optee.config @@ -0,0 +1,2 @@ +CONFIG_TEE=y +CONFIG_OPTEE=y diff --git a/target/linux/phytium/files-5.10/arch/arm64/configs/phytiumpi_firefly_defconfig b/target/linux/phytium/files-5.10/arch/arm64/configs/phytiumpi_firefly_defconfig new file mode 100644 index 000000000..243a07f01 --- /dev/null +++ b/target/linux/phytium/files-5.10/arch/arm64/configs/phytiumpi_firefly_defconfig @@ -0,0 +1,738 @@ +CONFIG_LOCALVERSION="-phytium-embeded-2023-v1.0-GA" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y +CONFIG_AUDIT=y +CONFIG_NO_HZ_IDLE=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_PREEMPT=y +CONFIG_IRQ_TIME_ACCOUNTING=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_BSD_PROCESS_ACCT_V3=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_MEMCG=y +CONFIG_BLK_CGROUP=y +CONFIG_CGROUP_PIDS=y +CONFIG_CGROUP_HUGETLB=y +CONFIG_CPUSETS=y +CONFIG_CGROUP_DEVICE=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_CGROUP_PERF=y +CONFIG_USER_NS=y +CONFIG_SCHED_AUTOGROUP=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_KALLSYMS_ALL=y +# CONFIG_COMPAT_BRK is not set +CONFIG_PROFILING=y +CONFIG_ARCH_PHYTIUM=y +CONFIG_ARM64_VA_BITS_48=y +CONFIG_SCHED_MC=y +CONFIG_SCHED_SMT=y +CONFIG_KEXEC=y +CONFIG_KEXEC_FILE=y +CONFIG_CRASH_DUMP=y +CONFIG_XEN=y +CONFIG_COMPAT=y +CONFIG_RANDOMIZE_BASE=y +CONFIG_HIBERNATION=y +CONFIG_WQ_POWER_EFFICIENT_DEFAULT=y +CONFIG_ENERGY_MODEL=y +CONFIG_ARM_CPUIDLE=y +CONFIG_ARM_PSCI_CPUIDLE=y +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=m +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=m +CONFIG_CPUFREQ_DT=y +CONFIG_ACPI_CPPC_CPUFREQ=m +CONFIG_ARM_SCPI_CPUFREQ=y +CONFIG_ARM_SCMI_CPUFREQ=y +CONFIG_ARM_SCMI_PROTOCOL=y +CONFIG_ARM_SCPI_PROTOCOL=y +CONFIG_EFI_CAPSULE_LOADER=y +CONFIG_ACPI=y +CONFIG_ACPI_APEI=y +CONFIG_ACPI_APEI_GHES=y +CONFIG_ACPI_APEI_MEMORY_FAILURE=y +CONFIG_ACPI_APEI_EINJ=y +CONFIG_VIRTUALIZATION=y +CONFIG_KVM=y +CONFIG_ARM64_CRYPTO=y +CONFIG_CRYPTO_SHA1_ARM64_CE=y +CONFIG_CRYPTO_SHA2_ARM64_CE=y +CONFIG_CRYPTO_SHA512_ARM64_CE=m +CONFIG_CRYPTO_SHA3_ARM64=m +CONFIG_CRYPTO_SM3_ARM64_CE=m +CONFIG_CRYPTO_GHASH_ARM64_CE=y +CONFIG_CRYPTO_CRCT10DIF_ARM64_CE=m +CONFIG_CRYPTO_AES_ARM64_CE_CCM=y +CONFIG_CRYPTO_AES_ARM64_CE_BLK=y +CONFIG_CRYPTO_CHACHA20_NEON=m +CONFIG_CRYPTO_AES_ARM64_BS=m +CONFIG_KPROBES=y +CONFIG_JUMP_LABEL=y +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_BLK_DEV_INTEGRITY=y +CONFIG_PARTITION_ADVANCED=y +CONFIG_CMDLINE_PARTITION=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_KSM=y +CONFIG_MEMORY_FAILURE=y +CONFIG_TRANSPARENT_HUGEPAGE=y +CONFIG_CMA=y +CONFIG_CMA_AREAS=19 +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IPV6=m +CONFIG_NETFILTER=y +CONFIG_NF_CONNTRACK=m +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NETFILTER_XT_TARGET_CHECKSUM=m +CONFIG_NETFILTER_XT_TARGET_LOG=m +CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=m +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=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_MANGLE=m +CONFIG_IP6_NF_IPTABLES=m +CONFIG_IP6_NF_FILTER=m +CONFIG_IP6_NF_TARGET_REJECT=m +CONFIG_IP6_NF_MANGLE=m +CONFIG_IP6_NF_NAT=m +CONFIG_IP6_NF_TARGET_MASQUERADE=m +CONFIG_BRIDGE=m +CONFIG_BRIDGE_VLAN_FILTERING=y +CONFIG_NET_DSA=m +CONFIG_NET_DSA_TAG_OCELOT=m +CONFIG_VLAN_8021Q=m +CONFIG_VLAN_8021Q_GVRP=y +CONFIG_VLAN_8021Q_MVRP=y +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_CBS=m +CONFIG_NET_SCH_ETF=m +CONFIG_NET_SCH_TAPRIO=m +CONFIG_NET_SCH_MQPRIO=m +CONFIG_NET_SCH_INGRESS=m +CONFIG_NET_CLS_BASIC=m +CONFIG_NET_CLS_U32=m +CONFIG_NET_CLS_FLOWER=m +CONFIG_NET_CLS_ACT=y +CONFIG_NET_ACT_GACT=m +CONFIG_NET_ACT_MIRRED=m +CONFIG_NET_ACT_SKBEDIT=m +CONFIG_NET_ACT_GATE=m +CONFIG_QRTR=m +CONFIG_QRTR_SMD=m +CONFIG_QRTR_TUN=m +CONFIG_BPF_JIT=y +CONFIG_CAN=m +CONFIG_CAN_FLEXCAN=m +CONFIG_CAN_PHYTIUM=m +CONFIG_CAN_PHYTIUM_PLATFORM=m +CONFIG_BT=y +CONFIG_BT_RFCOMM=m +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=m +CONFIG_BT_HIDP=m +# CONFIG_BT_LE is not set +CONFIG_BT_LEDS=y +# CONFIG_BT_DEBUGFS is not set +CONFIG_BT_HCIBTUSB=m +CONFIG_BT_HCIUART=y +CONFIG_BT_HCIUART_3WIRE=y +CONFIG_CFG80211=y +CONFIG_CFG80211_WEXT=y +CONFIG_MAC80211=y +CONFIG_MAC80211_LEDS=y +CONFIG_RFKILL=y +CONFIG_NET_9P=y +CONFIG_NET_9P_VIRTIO=y +CONFIG_NFC=m +CONFIG_NFC_NCI=m +CONFIG_NFC_S3FWRN5_I2C=m +CONFIG_PCI=y +CONFIG_PCIEPORTBUS=y +CONFIG_HOTPLUG_PCI_PCIE=y +CONFIG_PCI_IOV=y +CONFIG_PCI_PASID=y +CONFIG_HOTPLUG_PCI=y +CONFIG_HOTPLUG_PCI_ACPI=y +CONFIG_PCI_HOST_GENERIC=y +CONFIG_PCI_XGENE=y +CONFIG_PCIE_ALTERA=y +CONFIG_PCIE_ALTERA_MSI=y +CONFIG_PCI_HOST_THUNDER_PEM=y +CONFIG_PCI_HOST_THUNDER_ECAM=y +CONFIG_PCIE_LAYERSCAPE_GEN4=y +CONFIG_PCI_ENDPOINT=y +CONFIG_PCI_ENDPOINT_CONFIGFS=y +CONFIG_PCI_EPF_TEST=m +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_FW_LOADER_USER_HELPER=y +CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y +CONFIG_BRCMSTB_GISB_ARB=y +CONFIG_SIMPLE_PM_BUS=y +CONFIG_VEXPRESS_CONFIG=y +CONFIG_MTD=y +CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_BLOCK=y +CONFIG_MTD_DATAFLASH=y +CONFIG_MTD_SST25L=y +CONFIG_MTD_RAW_NAND=y +CONFIG_MTD_NAND_DENALI_DT=y +CONFIG_MTD_SPI_NOR=y +CONFIG_SPI_PHYTIUM_QUADSPI=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_NBD=m +CONFIG_BLK_DEV_RAM=y +CONFIG_VIRTIO_BLK=y +CONFIG_BLK_DEV_NVME=y +CONFIG_NVME_MULTIPATH=y +CONFIG_NVME_HWMON=y +CONFIG_NVME_FC=y +CONFIG_NVME_TCP=y +CONFIG_NVME_TARGET=y +CONFIG_NVME_TARGET_PASSTHRU=y +CONFIG_NVME_TARGET_LOOP=y +CONFIG_NVME_TARGET_FC=y +CONFIG_NVME_TARGET_FCLOOP=y +CONFIG_NVME_TARGET_TCP=y +CONFIG_SRAM=y +CONFIG_PCI_ENDPOINT_TEST=m +CONFIG_EEPROM_AT24=m +CONFIG_EEPROM_AT25=m +CONFIG_UACCE=m +# CONFIG_SCSI_PROC_FS is not set +CONFIG_BLK_DEV_SD=y +CONFIG_SCSI_SAS_LIBSAS=y +CONFIG_SCSI_SAS_ATA=y +CONFIG_MEGARAID_SAS=y +CONFIG_SCSI_MPT3SAS=m +CONFIG_SCSI_UFSHCD=y +CONFIG_SCSI_UFSHCD_PLATFORM=y +CONFIG_ATA=y +CONFIG_SATA_AHCI=y +CONFIG_SATA_AHCI_PLATFORM=y +CONFIG_AHCI_CEVA=y +CONFIG_AHCI_XGENE=y +CONFIG_AHCI_QORIQ=y +CONFIG_SATA_SIL24=y +CONFIG_PATA_PLATFORM=y +CONFIG_PATA_OF_PLATFORM=y +CONFIG_MD=y +CONFIG_BLK_DEV_MD=m +CONFIG_BLK_DEV_DM=m +CONFIG_DM_MIRROR=m +CONFIG_DM_ZERO=m +CONFIG_NETDEVICES=y +CONFIG_MACVLAN=m +CONFIG_MACVTAP=m +CONFIG_TUN=y +CONFIG_VETH=m +CONFIG_VIRTIO_NET=y +CONFIG_AMD_XGBE=y +CONFIG_ATL1C=m +CONFIG_BCMGENET=m +CONFIG_BNX2X=m +CONFIG_MACB=y +CONFIG_THUNDER_NIC_PF=y +# CONFIG_NET_VENDOR_HISILICON is not set +# CONFIG_NET_VENDOR_HUAWEI is not set +CONFIG_E1000=m +CONFIG_E1000E=m +CONFIG_IGB=m +CONFIG_IGBVF=m +CONFIG_IXGB=m +CONFIG_IXGBE=m +CONFIG_IXGBEVF=m +CONFIG_I40E=m +CONFIG_I40EVF=m +CONFIG_MVMDIO=y +CONFIG_SKY2=y +CONFIG_MLX4_EN=m +CONFIG_MLX5_CORE=m +CONFIG_MLX5_CORE_EN=y +CONFIG_QCOM_EMAC=m +CONFIG_RMNET=m +CONFIG_SMC91X=y +CONFIG_SMSC911X=y +CONFIG_STMMAC_ETH=m +CONFIG_AQUANTIA_PHY=y +CONFIG_BROADCOM_PHY=m +CONFIG_MARVELL_PHY=m +CONFIG_MARVELL_10G_PHY=m +CONFIG_MICREL_PHY=y +CONFIG_MICROSEMI_PHY=y +CONFIG_AT803X_PHY=y +CONFIG_REALTEK_PHY=m +CONFIG_ROCKCHIP_PHY=y +CONFIG_VITESSE_PHY=y +CONFIG_MOTORCOMM_PHY=y +CONFIG_MDIO_BITBANG=y +CONFIG_MDIO_BUS_MUX_MULTIPLEXER=y +CONFIG_MDIO_BUS_MUX_MMIOREG=y +CONFIG_PPP=y +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_ASYNC=y +CONFIG_PPP_SYNC_TTY=y +CONFIG_USB_PEGASUS=m +CONFIG_USB_RTL8150=m +CONFIG_USB_RTL8152=m +CONFIG_USB_LAN78XX=m +CONFIG_USB_USBNET=y +CONFIG_USB_NET_AX8817X=m +CONFIG_USB_NET_AX88179_178A=m +CONFIG_USB_NET_CDCETHER=m +CONFIG_USB_NET_CDC_NCM=m +CONFIG_USB_NET_DM9601=m +CONFIG_USB_NET_SR9800=m +CONFIG_USB_NET_SMSC75XX=m +CONFIG_USB_NET_SMSC95XX=m +CONFIG_USB_NET_NET1080=m +CONFIG_USB_NET_PLUSB=m +CONFIG_USB_NET_MCS7830=m +CONFIG_USB_NET_CDC_SUBSET=m +CONFIG_USB_NET_ZAURUS=m +CONFIG_USB_NET_QMI_WWAN=y +CONFIG_ATH10K=m +CONFIG_ATH10K_PCI=m +CONFIG_BRCMFMAC=m +CONFIG_MWIFIEX=m +CONFIG_MWIFIEX_PCIE=m +CONFIG_RTL_CARDS=m +CONFIG_WL18XX=m +CONFIG_WLCORE_SDIO=m +CONFIG_RTL8821CS=m +CONFIG_INPUT_EVDEV=y +CONFIG_KEYBOARD_ADC=m +CONFIG_KEYBOARD_GPIO=y +CONFIG_KEYBOARD_CROS_EC=y +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_ATMEL_MXT=m +CONFIG_INPUT_MISC=y +# CONFIG_SERIO_SERPORT is not set +CONFIG_SERIO_AMBAKMI=y +CONFIG_LEGACY_PTY_COUNT=16 +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_EXTENDED=y +CONFIG_SERIAL_8250_SHARE_IRQ=y +CONFIG_SERIAL_8250_DW=y +CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SERIAL_AMBA_PL011=y +CONFIG_SERIAL_AMBA_PL011_CONSOLE=y +CONFIG_SERIAL_XILINX_PS_UART=y +CONFIG_SERIAL_XILINX_PS_UART_CONSOLE=y +CONFIG_SERIAL_FSL_LPUART=y +CONFIG_SERIAL_FSL_LPUART_CONSOLE=y +CONFIG_SERIAL_FSL_LINFLEXUART=y +CONFIG_SERIAL_FSL_LINFLEXUART_CONSOLE=y +CONFIG_SERIAL_DEV_BUS=y +CONFIG_VIRTIO_CONSOLE=y +CONFIG_IPMI_HANDLER=m +CONFIG_IPMI_DEVICE_INTERFACE=m +CONFIG_IPMI_SI=m +CONFIG_HW_RANDOM=y +# CONFIG_HW_RANDOM_HISI_V2 is not set +CONFIG_HW_RANDOM_PHYTIUM=y +CONFIG_TCG_TPM=y +CONFIG_TCG_TIS_I2C_INFINEON=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_MUX=y +CONFIG_I2C_MUX_PCA954x=y +CONFIG_I2C_GPIO=m +CONFIG_I2C_PHYTIUM_PLATFORM=y +CONFIG_I3C=m +CONFIG_SPI=y +CONFIG_SPI_BITBANG=m +CONFIG_SPI_PHYTIUM_PLAT=y +CONFIG_SPI_SPIDEV=y +CONFIG_SPMI=y +CONFIG_PINCTRL=y +CONFIG_PINCTRL_SINGLE=y +CONFIG_PINCTRL_MAX77620=y +CONFIG_GPIO_ALTERA=m +CONFIG_GPIO_DWAPB=y +CONFIG_GPIO_MB86S7X=y +CONFIG_GPIO_PL061=y +CONFIG_GPIO_WCD934X=m +CONFIG_GPIO_XGENE=y +# CONFIG_GPIO_PHYTIUM_SGPIO is not set +CONFIG_GPIO_MAX732X=y +CONFIG_GPIO_PCA953X=y +CONFIG_GPIO_PCA953X_IRQ=y +CONFIG_GPIO_BD9571MWV=m +CONFIG_GPIO_MAX77620=y +CONFIG_W1=m +CONFIG_W1_SLAVE_THERM=m +CONFIG_POWER_RESET_BRCMSTB=y +CONFIG_POWER_RESET_XGENE=y +CONFIG_POWER_RESET_SYSCON=y +CONFIG_SYSCON_REBOOT_MODE=y +CONFIG_BATTERY_SBS=m +CONFIG_BATTERY_BQ27XXX=y +CONFIG_SENSORS_ARM_SCMI=y +CONFIG_SENSORS_ARM_SCPI=y +CONFIG_SENSORS_LM90=m +CONFIG_SENSORS_PWM_FAN=m +CONFIG_SENSORS_INA2XX=m +CONFIG_SENSORS_INA3221=m +CONFIG_SENSORS_PHYTIUM=m +CONFIG_THERMAL_GOV_POWER_ALLOCATOR=y +CONFIG_CPU_THERMAL=y +CONFIG_THERMAL_EMULATION=y +CONFIG_WATCHDOG=y +CONFIG_ARM_SP805_WATCHDOG=y +CONFIG_ARM_SBSA_WATCHDOG=y +CONFIG_DW_WATCHDOG=y +CONFIG_ARM_SMC_WATCHDOG=y +CONFIG_MFD_BD9571MWV=y +CONFIG_MFD_AXP20X_I2C=y +CONFIG_MFD_HI6421_PMIC=y +CONFIG_MFD_MAX77620=y +CONFIG_MFD_RK808=y +CONFIG_MFD_SEC_CORE=y +CONFIG_MFD_ROHM_BD718XX=y +CONFIG_MFD_WCD934X=m +CONFIG_REGULATOR=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +CONFIG_REGULATOR_AXP20X=y +CONFIG_REGULATOR_BD718XX=y +CONFIG_REGULATOR_BD9571MWV=y +CONFIG_REGULATOR_FAN53555=y +CONFIG_REGULATOR_GPIO=y +CONFIG_REGULATOR_HI6421V530=y +CONFIG_REGULATOR_MAX77620=y +CONFIG_REGULATOR_MAX8973=y +CONFIG_REGULATOR_PCA9450=y +CONFIG_REGULATOR_PFUZE100=y +CONFIG_REGULATOR_PWM=y +CONFIG_REGULATOR_QCOM_SPMI=y +CONFIG_REGULATOR_RK808=y +CONFIG_REGULATOR_S2MPS11=y +CONFIG_REGULATOR_VCTRL=m +CONFIG_RC_CORE=m +CONFIG_RC_DECODERS=y +CONFIG_RC_DEVICES=y +CONFIG_MEDIA_SUPPORT=m +CONFIG_MEDIA_CAMERA_SUPPORT=y +CONFIG_MEDIA_ANALOG_TV_SUPPORT=y +CONFIG_MEDIA_DIGITAL_TV_SUPPORT=y +CONFIG_MEDIA_SDR_SUPPORT=y +CONFIG_MEDIA_PLATFORM_SUPPORT=y +# CONFIG_DVB_NET is not set +CONFIG_MEDIA_USB_SUPPORT=y +CONFIG_USB_VIDEO_CLASS=m +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_V4L_MEM2MEM_DRIVERS=y +CONFIG_SDR_PLATFORM_DRIVERS=y +CONFIG_VIDEO_IMX219=m +CONFIG_VIDEO_OV5645=m +CONFIG_DRM=y +CONFIG_DRM_I2C_CH7006=m +CONFIG_DRM_I2C_SIL164=m +CONFIG_DRM_I2C_NXP_TDA998X=m +CONFIG_DRM_MALI_DISPLAY=m +CONFIG_DRM_RCAR_DW_HDMI=m +CONFIG_DRM_RCAR_LVDS=m +CONFIG_DRM_PANEL_LVDS=m +CONFIG_DRM_PANEL_SIMPLE=m +CONFIG_DRM_PANEL_RAYDIUM_RM67191=m +CONFIG_DRM_PANEL_SITRONIX_ST7703=m +CONFIG_DRM_PANEL_TRULY_NT35597_WQXGA=m +CONFIG_DRM_DISPLAY_CONNECTOR=m +CONFIG_DRM_LONTIUM_LT9611=m +CONFIG_DRM_NWL_MIPI_DSI=m +CONFIG_DRM_SII902X=m +CONFIG_DRM_SIMPLE_BRIDGE=m +CONFIG_DRM_THINE_THC63LVD1024=m +CONFIG_DRM_TI_SN65DSI86=m +CONFIG_DRM_I2C_ADV7511=m +CONFIG_DRM_I2C_ADV7511_AUDIO=y +CONFIG_DRM_DW_HDMI_AHB_AUDIO=m +CONFIG_DRM_DW_HDMI_I2S_AUDIO=m +CONFIG_DRM_DW_HDMI_CEC=m +CONFIG_DRM_PHYTIUM=y +CONFIG_DRM_LEGACY=y +CONFIG_FB_MODE_HELPERS=y +CONFIG_FB_EFI=y +CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_BACKLIGHT_PWM=m +CONFIG_BACKLIGHT_LP855X=m +CONFIG_LOGO=y +# CONFIG_LOGO_LINUX_MONO is not set +# CONFIG_LOGO_LINUX_VGA16 is not set +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_HDA_INTEL=m +CONFIG_SND_HDA_HWDEP=y +CONFIG_SND_HDA_INPUT_BEEP=y +CONFIG_SND_HDA_PATCH_LOADER=y +CONFIG_SND_HDA_CODEC_REALTEK=m +CONFIG_SND_HDA_CODEC_HDMI=m +CONFIG_SND_SOC=y +CONFIG_SND_SOC_FSL_SAI=m +CONFIG_SND_SOC_PHYTIUM_I2S=y +CONFIG_SND_PMDK_ES8388=y +CONFIG_SND_PMDK_ES8336=y +CONFIG_SND_PMDK_DP=y +CONFIG_SND_SOC_AK4613=m +CONFIG_SND_SOC_CROS_EC_CODEC=m +CONFIG_SND_SOC_DMIC=m +CONFIG_SND_SOC_ES7134=m +CONFIG_SND_SOC_ES7241=m +CONFIG_SND_SOC_MAX98357A=m +CONFIG_SND_SOC_MAX98927=m +CONFIG_SND_SOC_PCM3168A_I2C=m +CONFIG_SND_SOC_SIMPLE_AMPLIFIER=m +CONFIG_SND_SOC_SPDIF=m +CONFIG_SND_SOC_TAS571X=m +CONFIG_SND_SOC_WCD934X=m +CONFIG_SND_SOC_WM8904=m +CONFIG_SND_SOC_WSA881X=m +CONFIG_SND_SIMPLE_CARD=y +CONFIG_SND_AUDIO_GRAPH_CARD=y +CONFIG_I2C_HID=m +CONFIG_USB_CONN_GPIO=y +CONFIG_USB=y +CONFIG_USB_OTG=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_ACM=y +CONFIG_USB_STORAGE=y +CONFIG_USB_MUSB_HDRC=y +CONFIG_USB_DWC3=y +CONFIG_USB_DWC2=y +CONFIG_USB_CHIPIDEA=y +CONFIG_USB_CHIPIDEA_UDC=y +CONFIG_USB_CHIPIDEA_HOST=y +CONFIG_USB_ISP1760=y +CONFIG_USB_PHYTIUM=y +CONFIG_USB_SERIAL=y +CONFIG_USB_SERIAL_CH341=y +CONFIG_USB_SERIAL_FTDI_SIO=m +CONFIG_USB_SERIAL_OPTION=y +CONFIG_USB_HSIC_USB3503=y +CONFIG_NOP_USB_XCEIV=y +CONFIG_USB_ULPI=y +CONFIG_USB_GADGET=y +CONFIG_USB_SNP_UDC_PLAT=y +CONFIG_USB_BDC_UDC=y +CONFIG_USB_CONFIGFS=m +CONFIG_USB_CONFIGFS_SERIAL=y +CONFIG_USB_CONFIGFS_ACM=y +CONFIG_USB_CONFIGFS_OBEX=y +CONFIG_USB_CONFIGFS_NCM=y +CONFIG_USB_CONFIGFS_ECM=y +CONFIG_USB_CONFIGFS_ECM_SUBSET=y +CONFIG_USB_CONFIGFS_RNDIS=y +CONFIG_USB_CONFIGFS_EEM=y +CONFIG_USB_CONFIGFS_MASS_STORAGE=y +CONFIG_USB_CONFIGFS_F_FS=y +CONFIG_TYPEC=m +CONFIG_TYPEC_TCPM=m +CONFIG_TYPEC_FUSB302=m +CONFIG_TYPEC_HD3SS3220=m +CONFIG_MMC=y +CONFIG_MMC_BLOCK_MINORS=32 +CONFIG_MMC_ARMMMCI=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_ACPI=y +CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_MMC_SDHCI_OF_ARASAN=y +CONFIG_MMC_SDHCI_CADENCE=y +CONFIG_MMC_SDHCI_F_SDH30=y +CONFIG_MMC_SPI=y +CONFIG_MMC_DW=y +CONFIG_MMC_DW_EXYNOS=y +CONFIG_MMC_DW_HI3798CV200=y +CONFIG_MMC_DW_K3=y +CONFIG_MMC_SDHCI_XENON=y +CONFIG_MMC_SDHCI_AM654=y +CONFIG_LEDS_CLASS=y +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_PWM=y +CONFIG_LEDS_SYSCON=y +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_LEDS_TRIGGER_DISK=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +CONFIG_LEDS_TRIGGER_CPU=y +CONFIG_LEDS_TRIGGER_DEFAULT_ON=y +CONFIG_LEDS_TRIGGER_PANIC=y +CONFIG_EDAC=y +CONFIG_EDAC_GHES=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_DS1307=m +CONFIG_RTC_DRV_MAX77686=y +CONFIG_RTC_DRV_RK808=m +CONFIG_RTC_DRV_PCF85363=m +CONFIG_RTC_DRV_RX8581=m +CONFIG_RTC_DRV_RV8803=m +CONFIG_RTC_DRV_S5M=y +CONFIG_RTC_DRV_SD3068=m +CONFIG_RTC_DRV_DS3232=y +CONFIG_RTC_DRV_PCF2127=m +CONFIG_RTC_DRV_EFI=y +CONFIG_RTC_DRV_CROS_EC=y +CONFIG_RTC_DRV_PL031=y +CONFIG_DMADEVICES=y +CONFIG_BCM_SBA_RAID=m +CONFIG_FSL_EDMA=y +CONFIG_MV_XOR_V2=y +CONFIG_PL330_DMA=y +CONFIG_UIO=m +CONFIG_UIO_CIF=m +CONFIG_UIO_PDRV_GENIRQ=m +CONFIG_UIO_DMEM_GENIRQ=m +CONFIG_UIO_AEC=m +CONFIG_UIO_SERCOS3=m +CONFIG_UIO_PCI_GENERIC=m +CONFIG_UIO_NETX=m +CONFIG_UIO_PRUSS=m +CONFIG_UIO_MF624=m +CONFIG_VFIO=y +CONFIG_VFIO_PCI=y +CONFIG_VIRTIO_PCI=y +CONFIG_VIRTIO_BALLOON=y +CONFIG_VIRTIO_MMIO=y +CONFIG_XEN_GNTDEV=y +CONFIG_XEN_GRANT_DEV_ALLOC=y +CONFIG_STAGING=y +CONFIG_CHROME_PLATFORMS=y +CONFIG_CROS_EC=y +CONFIG_CROS_EC_I2C=y +CONFIG_CROS_EC_SPI=y +CONFIG_CROS_EC_CHARDEV=m +CONFIG_COMMON_CLK_RK808=y +CONFIG_COMMON_CLK_SCPI=y +CONFIG_COMMON_CLK_CS2000_CP=y +CONFIG_COMMON_CLK_S2MPS11=y +CONFIG_CLK_QORIQ=y +CONFIG_COMMON_CLK_XGENE=y +CONFIG_COMMON_CLK_PWM=y +CONFIG_COMMON_CLK_VC5=y +CONFIG_COMMON_CLK_BD718XX=m +CONFIG_HWSPINLOCK=y +CONFIG_HWSPINLOCK_PHYTIUM=y +CONFIG_ARM_MHU=y +CONFIG_PLATFORM_MHU=y +CONFIG_PHYTIUM_MBOX=y +CONFIG_ARM_SMMU=y +CONFIG_ARM_SMMU_V3=y +CONFIG_REMOTEPROC=y +CONFIG_RPMSG_QCOM_GLINK_RPM=y +CONFIG_SOUNDWIRE=m +CONFIG_SOUNDWIRE_QCOM=m +CONFIG_SOC_BRCMSTB=y +CONFIG_SOC_TI=y +CONFIG_EXTCON_PTN5150=m +CONFIG_EXTCON_USB_GPIO=y +CONFIG_EXTCON_USBC_CROS_EC=y +CONFIG_MEMORY=y +CONFIG_IIO=y +CONFIG_MAX9611=m +CONFIG_QCOM_SPMI_ADC5=m +CONFIG_PHYTIUM_ADC=m +CONFIG_IIO_CROS_EC_SENSORS_CORE=m +CONFIG_IIO_CROS_EC_SENSORS=m +CONFIG_IIO_CROS_EC_LIGHT_PROX=m +CONFIG_SENSORS_ISL29018=m +CONFIG_IIO_CROS_EC_BARO=m +CONFIG_MPL3115=m +CONFIG_PWM=y +CONFIG_PWM_CROS_EC=m +CONFIG_PWM_PHYTIUM=m +CONFIG_PHY_XGENE=y +CONFIG_PHY_FSL_IMX8MQ_USB=y +CONFIG_PHY_MIXEL_MIPI_DPHY=m +CONFIG_PHY_QCOM_USB_HS=y +CONFIG_PHY_SAMSUNG_USB2=y +CONFIG_ARM_SMMU_V3_PMU=m +CONFIG_FPGA=y +CONFIG_FPGA_BRIDGE=m +CONFIG_ALTERA_FREEZE_BRIDGE=m +CONFIG_FPGA_REGION=m +CONFIG_OF_FPGA_REGION=m +CONFIG_MUX_MMIO=y +CONFIG_SLIM_QCOM_CTRL=m +CONFIG_INTERCONNECT=y +CONFIG_EXT2_FS=y +CONFIG_EXT3_FS=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_BTRFS_FS=m +CONFIG_BTRFS_FS_POSIX_ACL=y +CONFIG_EXPORTFS_BLOCK_OPS=y +CONFIG_FANOTIFY=y +CONFIG_FANOTIFY_ACCESS_PERMISSIONS=y +CONFIG_QUOTA=y +CONFIG_AUTOFS4_FS=y +CONFIG_FUSE_FS=m +CONFIG_CUSE=m +CONFIG_OVERLAY_FS=m +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_HUGETLBFS=y +CONFIG_EFIVAR_FS=y +CONFIG_SQUASHFS=y +CONFIG_SQUASHFS_XATTR=y +CONFIG_SQUASHFS_LZ4=y +CONFIG_SQUASHFS_LZO=y +CONFIG_SQUASHFS_XZ=y +CONFIG_SQUASHFS_ZSTD=y +CONFIG_SQUASHFS_4K_DEVBLK_SIZE=y +CONFIG_SQUASHFS_EMBEDDED=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=y +CONFIG_SECURITY=y +CONFIG_CRYPTO_USER=y +CONFIG_CRYPTO_ECHAINIV=y +CONFIG_CRYPTO_MD5=y +CONFIG_CRYPTO_ANSI_CPRNG=y +CONFIG_CRYPTO_DRBG_HASH=y +CONFIG_CRYPTO_USER_API_HASH=y +CONFIG_CRYPTO_USER_API_SKCIPHER=y +CONFIG_CRYPTO_USER_API_RNG=m +CONFIG_CRYPTO_USER_API_AEAD=y +CONFIG_CRYPTO_DEV_CCREE=m +CONFIG_CRYPTO_DEV_HISI_SEC2=m +CONFIG_CRYPTO_DEV_HISI_ZIP=m +CONFIG_CRYPTO_DEV_HISI_HPRE=m +CONFIG_CRYPTO_DEV_AMLOGIC_GXL=m +CONFIG_INDIRECT_PIO=y +CONFIG_DMA_CMA=y +CONFIG_DMA_PERNUMA_CMA=y +CONFIG_CMA_SIZE_MBYTES=32 +CONFIG_PRINTK_TIME=y +CONFIG_DEBUG_INFO=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_FS=y +CONFIG_DEBUG_KERNEL=y +# CONFIG_SCHED_DEBUG is not set +# CONFIG_DEBUG_PREEMPT is not set +# CONFIG_FTRACE is not set +CONFIG_MEMTEST=y diff --git a/target/linux/phytium/files-5.10/drivers/char/hw_random/phytium-rng.c b/target/linux/phytium/files-5.10/drivers/char/hw_random/phytium-rng.c new file mode 100644 index 000000000..5b5e89651 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/char/hw_random/phytium-rng.c @@ -0,0 +1,155 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Phytium SoC RNG Driver + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TRNG_CR 0x00 +#define TRNG_CR_RNGEN BIT(0) +#define TRNG_CR_ROSEN_MASK GENMASK(7, 4) +#define TRNG_CR_DIEN BIT(16) +#define TRNG_CR_ERIEN BIT(17) +#define TRNG_CR_IRQEN BIT(24) +#define TRNG_MSEL 0x04 +#define TRNG_MSEL_MSEL BIT(0) +#define TRNG_SR 0x08 +#define TRNG_SR_HTF BIT(0) +#define TRNG_SR_DRDY BIT(1) +#define TRNG_SR_ERERR BIT(3) +#define TRNG_DR 0x0C +#define TRNG_RESEED 0x40 +#define TRNG_RESEED_RSED BIT(0) + +#define DELAY 10 +#define TIMEOUT 100 + +static int msel; +module_param(msel, int, 0444); +MODULE_PARM_DESC(msel, "Phytium RNG mode selection: 0 - TRNG. 1 - PRNG."); + +struct phytium_rng { + struct hwrng rng; + void __iomem *base; +}; + +static int phytium_rng_init(struct hwrng *rng) +{ + struct phytium_rng *priv = container_of(rng, struct phytium_rng, rng); + u32 reg; + + /* Mode Selection */ + reg = msel ? TRNG_MSEL_MSEL : 0; + writel(reg, priv->base + TRNG_MSEL); + + /* If PRGN mode is on, do reseed operations */ + if (msel) + writel(TRNG_RESEED_RSED, priv->base + TRNG_RESEED); + + /* Clear status */ + writel(0x7, priv->base + TRNG_SR); + + /* Enable TRNG */ + reg = readl(priv->base + TRNG_CR) | TRNG_CR_ROSEN_MASK | TRNG_CR_RNGEN; + writel(reg, priv->base + TRNG_CR); + + return 0; +} + +static void phytium_rng_cleanup(struct hwrng *rng) +{ + struct phytium_rng *priv = container_of(rng, struct phytium_rng, rng); + + writel(0x7, priv->base + TRNG_SR); +} + +static int phytium_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) +{ + struct phytium_rng *priv = container_of(rng, struct phytium_rng, rng); + u32 reg; + int ret = 0; + + /* TRNG can generate at most 8*32bit random number per time */ + max = max > 32 ? 32 : max; + + reg = readl(priv->base + TRNG_SR); + if (!(reg & TRNG_SR_DRDY) && wait) { + ret = readl_poll_timeout(priv->base + TRNG_SR, reg, + reg & TRNG_SR_DRDY, DELAY, TIMEOUT); + if (ret) { + dev_err((struct device *)priv->rng.priv, + "%s: timeout %x!\n", __func__, reg); + return -EIO; + } + } + + while (max >= 4) { + *(u32 *)buf = readl(priv->base + TRNG_DR); + + ret += sizeof(u32); + buf += sizeof(u32); + max -= sizeof(u32); + } + + /* Clear DRDY by writing 1 */ + writel(reg | TRNG_SR_DRDY, priv->base + TRNG_SR); + + return ret; +} + +static int phytium_rng_probe(struct platform_device *pdev) +{ + struct phytium_rng *priv; + struct resource *mem; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + platform_set_drvdata(pdev, priv); + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->base = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + priv->rng.name = pdev->name; + priv->rng.init = phytium_rng_init; + priv->rng.cleanup = phytium_rng_cleanup; + priv->rng.read = phytium_rng_read; + priv->rng.priv = (unsigned long)&pdev->dev; + priv->rng.quality = 1000; + + return devm_hwrng_register(&pdev->dev, &priv->rng); +} + +static const struct of_device_id phytium_rng_dt_ids[] = { + { .compatible = "phytium,rng" }, + { } +}; +MODULE_DEVICE_TABLE(of, phytium_rng_dt_ids); + +static struct platform_driver phytium_rng_driver = { + .probe = phytium_rng_probe, + .driver = { + .name = "phytium-rng", + .of_match_table = of_match_ptr(phytium_rng_dt_ids), + } +}; +module_platform_driver(phytium_rng_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Phytium random number generator driver"); +MODULE_AUTHOR("Chen Baozi "); diff --git a/target/linux/phytium/files-5.10/drivers/char/ipmi/bt_bmc_phytium.c b/target/linux/phytium/files-5.10/drivers/char/ipmi/bt_bmc_phytium.c new file mode 100755 index 000000000..8d650475b --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/char/ipmi/bt_bmc_phytium.c @@ -0,0 +1,533 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + * + * Derived from drivers/char/ipmi/bt-bmc.c + * Copyright (c) 2015-2016, IBM Corporation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * This is a BMC device used to communicate to the host + */ +#define DEVICE_NAME "ipmi-bt-host" + +#define BT_IO_BASE 0xe4 +#define BT_IRQ 10 + +#define LPC_HICR0 0x00 +#define LPC_HICR0_LPC3E BIT(7) +#define LPC_HICR0_LPC2E BIT(6) +#define LPC_HICR0_LPC1E BIT(5) +#define LPC_HICR0_SDWNE BIT(3) /* 0:disable, 1:enable for HICR1_SDWNB */ +#define LPC_HICR0_PMEE BIT(2) /* 0:disable, 1:enable for HICR1_PMEB */ + +#define LPC_HICR2 0x08 +#define LPC_HICR2_IBFIF3 BIT(3) /* 0:normal, 1:enable irq 3*/ + +#define LPC_HICR4 0x10 +#define LPC_HICR4_LADR12AS BIT(7) +#define LPC_HICR4_KCSENBL BIT(2) +#define LPC_BTENABLE BIT(0) /* share bt channel enable, 0:disable, 1:enable */ +#define LPC_LADR3H_BT 0x014 +#define LPC_LADR3L_BT 0x018 + +#define BT_CR1 0x4C +#define BT_CR1_IRQ_H2B 0x04 +#define BT_CR1_IRQ_HWRST 0x40 + +#define BT_CSR1 0x54 +#define BT_CSR1_IRQ_H2B 0x04 +#define BT_CSR1_IRQ_HWRST 0x40 + +#define BT_CTRL 0x58 +#define BT_CTRL_B_BUSY 0x80 +#define BT_CTRL_H_BUSY 0x40 +#define BT_CTRL_OEM0 0x20 +#define BT_CTRL_SMS_ATN 0x10 +#define BT_CTRL_B2H_ATN 0x08 +#define BT_CTRL_H2B_ATN 0x04 +#define BT_CTRL_CLR_RD_PTR 0x02 +#define BT_CTRL_CLR_WR_PTR 0x01 +#define BT_BMC2HOST 0x5C +#define BT_HOST2BMC 0x98 +#define BT_INTMASK 0x60 +#define BT_INTMASK_B2H_IRQEN 0x01 +#define BT_INTMASK_B2H_IRQ 0x02 +#define BT_INTMASK_BMC_HWRST 0x80 + +#define BT_BMC_BUFFER_SIZE 256 + +struct bt_bmc { + struct device dev; + struct miscdevice miscdev; + struct regmap *map; + int irq; + wait_queue_head_t queue; + struct timer_list poll_timer; + struct mutex mutex; +}; + +static atomic_t open_count = ATOMIC_INIT(0); + +static const struct regmap_config bt_regmap_cfg = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, +}; + +static u8 bt_inb(struct bt_bmc *bt_bmc, int reg) +{ + uint32_t val = 0; + int rc; + + rc = regmap_read(bt_bmc->map, reg, &val); + WARN(rc != 0, "regmap_read() failed: %d\n", rc); + + return rc == 0 ? (u8) val : 0; +} + +static void bt_outb(struct bt_bmc *bt_bmc, u8 data, int reg) +{ + int rc; + + rc = regmap_write(bt_bmc->map, reg, data); + WARN(rc != 0, "regmap_write() failed: %d\n", rc); +} + +static void clr_rd_ptr(struct bt_bmc *bt_bmc) +{ + bt_outb(bt_bmc, BT_CTRL_CLR_RD_PTR, BT_CTRL); +} + +static void clr_wr_ptr(struct bt_bmc *bt_bmc) +{ + bt_outb(bt_bmc, BT_CTRL_CLR_WR_PTR, BT_CTRL); +} + +static void clr_h2b_atn(struct bt_bmc *bt_bmc) +{ + bt_outb(bt_bmc, 0, BT_CTRL); +} + +static void set_b_busy(struct bt_bmc *bt_bmc) +{ + if (!(bt_inb(bt_bmc, BT_CTRL) & BT_CTRL_B_BUSY)) + bt_outb(bt_bmc, BT_CTRL_B_BUSY, BT_CTRL); +} + +static void clr_b_busy(struct bt_bmc *bt_bmc) +{ + if (bt_inb(bt_bmc, BT_CTRL) & BT_CTRL_B_BUSY) + bt_outb(bt_bmc, 0, BT_CTRL); +} + +static void set_b2h_atn(struct bt_bmc *bt_bmc) +{ + bt_outb(bt_bmc, BT_CTRL_B2H_ATN, BT_CTRL); +} + +static u8 bt_read(struct bt_bmc *bt_bmc) +{ + return bt_inb(bt_bmc, BT_HOST2BMC); +} + +static ssize_t bt_readn(struct bt_bmc *bt_bmc, u8 *buf, size_t n) +{ + int i; + + for (i = 0; i < n; i++) + buf[i] = bt_read(bt_bmc); + return n; +} + +static void bt_write(struct bt_bmc *bt_bmc, u8 c) +{ + bt_outb(bt_bmc, c, BT_BMC2HOST); +} + +static ssize_t bt_writen(struct bt_bmc *bt_bmc, u8 *buf, size_t n) +{ + int i; + + for (i = 0; i < n; i++) + bt_write(bt_bmc, buf[i]); + return n; +} + +static void set_sms_atn(struct bt_bmc *bt_bmc) +{ + bt_outb(bt_bmc, BT_CTRL_SMS_ATN, BT_CTRL); +} + +static struct bt_bmc *file_bt_bmc(struct file *file) +{ + return container_of(file->private_data, struct bt_bmc, miscdev); +} + +static int bt_bmc_open(struct inode *inode, struct file *file) +{ + struct bt_bmc *bt_bmc = file_bt_bmc(file); + + if (atomic_inc_return(&open_count) == 1) { + clr_b_busy(bt_bmc); + return 0; + } + + atomic_dec(&open_count); + + return -EBUSY; +} + +/* + * The BT (Block Transfer) interface means that entire messages are + * buffered by the host before a notification is sent to the BMC that + * there is data to be read. The first byte is the length and the + * message data follows. The read operation just tries to capture the + * whole before returning it to userspace. + * + * BT Message format : + * + * Byte 1 Byte 2 Byte 3 Byte 4 Byte 5:N + * Length NetFn/LUN Seq Cmd Data + * + */ +static ssize_t bt_bmc_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct bt_bmc *bt_bmc = file_bt_bmc(file); + u8 len; + int len_byte = 1; + u8 kbuffer[BT_BMC_BUFFER_SIZE]; + ssize_t ret = 0; + ssize_t nread; + + WARN_ON(*ppos); + + if (wait_event_interruptible(bt_bmc->queue, + bt_inb(bt_bmc, BT_CTRL) & BT_CTRL_H2B_ATN)) + return -ERESTARTSYS; + + mutex_lock(&bt_bmc->mutex); + + if (unlikely(!(bt_inb(bt_bmc, BT_CTRL) & BT_CTRL_H2B_ATN))) { + ret = -EIO; + goto out_unlock; + } + + set_b_busy(bt_bmc); + clr_h2b_atn(bt_bmc); + clr_rd_ptr(bt_bmc); + + /* + * The BT frames start with the message length, which does not + * include the length byte. + */ + kbuffer[0] = bt_read(bt_bmc); + len = kbuffer[0]; + + /* We pass the length back to userspace as well */ + if (len + 1 > count) + len = count - 1; + + while (len) { + nread = min_t(ssize_t, len, sizeof(kbuffer) - len_byte); + + bt_readn(bt_bmc, kbuffer + len_byte, nread); + + if (copy_to_user(buf, kbuffer, nread + len_byte)) { + ret = -EFAULT; + break; + } + len -= nread; + buf += nread + len_byte; + ret += nread + len_byte; + len_byte = 0; + } + + clr_b_busy(bt_bmc); + +out_unlock: + mutex_unlock(&bt_bmc->mutex); + return ret; +} + +/* + * BT Message response format : + * + * Byte 1 Byte 2 Byte 3 Byte 4 Byte 5 Byte 6:N + * Length NetFn/LUN Seq Cmd Code Data + */ +static ssize_t bt_bmc_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct bt_bmc *bt_bmc = file_bt_bmc(file); + u8 kbuffer[BT_BMC_BUFFER_SIZE]; + ssize_t ret = 0; + ssize_t nwritten; + + /* + * send a minimum response size + */ + if (count < 5) + return -EINVAL; + + WARN_ON(*ppos); + + /* + * There's no interrupt for clearing bmc busy so we have to + * poll + */ + if (wait_event_interruptible(bt_bmc->queue, + !(bt_inb(bt_bmc, BT_CTRL) & + (BT_CTRL_H_BUSY | BT_CTRL_B2H_ATN)))) + return -ERESTARTSYS; + + mutex_lock(&bt_bmc->mutex); + + if (unlikely(bt_inb(bt_bmc, BT_CTRL) & + (BT_CTRL_H_BUSY | BT_CTRL_B2H_ATN))) { + ret = -EIO; + goto out_unlock; + } + + clr_wr_ptr(bt_bmc); + + while (count) { + nwritten = min_t(ssize_t, count, sizeof(kbuffer)); + if (copy_from_user(&kbuffer, buf, nwritten)) { + ret = -EFAULT; + break; + } + + bt_writen(bt_bmc, kbuffer, nwritten); + + count -= nwritten; + buf += nwritten; + ret += nwritten; + } + + set_b2h_atn(bt_bmc); + +out_unlock: + mutex_unlock(&bt_bmc->mutex); + return ret; +} + +static long bt_bmc_ioctl(struct file *file, unsigned int cmd, + unsigned long param) +{ + struct bt_bmc *bt_bmc = file_bt_bmc(file); + + switch (cmd) { + case BT_BMC_IOCTL_SMS_ATN: + set_sms_atn(bt_bmc); + return 0; + } + return -EINVAL; +} + +static int bt_bmc_release(struct inode *inode, struct file *file) +{ + struct bt_bmc *bt_bmc = file_bt_bmc(file); + + atomic_dec(&open_count); + set_b_busy(bt_bmc); + return 0; +} + +static __poll_t bt_bmc_poll(struct file *file, poll_table *wait) +{ + struct bt_bmc *bt_bmc = file_bt_bmc(file); + __poll_t mask = 0; + u8 ctrl; + + poll_wait(file, &bt_bmc->queue, wait); + + ctrl = bt_inb(bt_bmc, BT_CTRL); + + if (ctrl & BT_CTRL_H2B_ATN) + mask |= EPOLLIN; + + if (!(ctrl & (BT_CTRL_H_BUSY | BT_CTRL_B2H_ATN))) + mask |= EPOLLOUT; + + return mask; +} + +static const struct file_operations bt_bmc_fops = { + .owner = THIS_MODULE, + .open = bt_bmc_open, + .read = bt_bmc_read, + .write = bt_bmc_write, + .release = bt_bmc_release, + .poll = bt_bmc_poll, + .unlocked_ioctl = bt_bmc_ioctl, +}; + +static void poll_timer(struct timer_list *t) +{ + struct bt_bmc *bt_bmc = from_timer(bt_bmc, t, poll_timer); + + bt_bmc->poll_timer.expires += msecs_to_jiffies(500); + wake_up(&bt_bmc->queue); + add_timer(&bt_bmc->poll_timer); +} + +static irqreturn_t bt_bmc_irq(int irq, void *arg) +{ + struct bt_bmc *bt_bmc = arg; + u32 reg, intmsk; + int rc; + + rc = regmap_read(bt_bmc->map, BT_CR1, ®); + if (rc) + return IRQ_NONE; + + reg &= BT_CR1_IRQ_H2B | BT_CR1_IRQ_HWRST; + if (!reg) + return IRQ_NONE; + + /* ack pending IRQs */ + regmap_write(bt_bmc->map, BT_CR1, 0); + + if (reg & BT_CR1_IRQ_HWRST) { + regmap_read(bt_bmc->map, BT_INTMASK, &intmsk); + if (intmsk & BT_INTMASK_BMC_HWRST) + regmap_write(bt_bmc->map, BT_INTMASK, 0); + } + + wake_up(&bt_bmc->queue); + return IRQ_HANDLED; +} + +static int bt_bmc_config_irq(struct bt_bmc *bt_bmc, + struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + int rc; + + bt_bmc->irq = platform_get_irq(pdev, 0); + if (!bt_bmc->irq) + return -ENODEV; + + rc = devm_request_irq(dev, bt_bmc->irq, bt_bmc_irq, IRQF_SHARED, + DEVICE_NAME, bt_bmc); + if (rc < 0) { + dev_warn(dev, "Unable to request IRQ %d\n", bt_bmc->irq); + bt_bmc->irq = 0; + return rc; + } + + /* + * Configure IRQs on the bmc clearing the H2B and HBUSY bits; + * H2B will be asserted when the bmc has data for us; HBUSY + * will be cleared (along with B2H) when we can write the next + * message to the BT buffer + */ + rc = regmap_update_bits(bt_bmc->map, BT_CSR1, + BT_CSR1_IRQ_H2B | BT_CSR1_IRQ_HWRST, + BT_CSR1_IRQ_H2B | BT_CSR1_IRQ_HWRST); + + return rc; +} + +static int bt_bmc_probe(struct platform_device *pdev) +{ + struct bt_bmc *bt_bmc; + struct device *dev; + int rc; + + if (!pdev || !pdev->dev.of_node) + return -ENODEV; + + dev = &pdev->dev; + dev_info(dev, "Found bt bmc device\n"); + + bt_bmc = devm_kzalloc(dev, sizeof(*bt_bmc), GFP_KERNEL); + if (!bt_bmc) + return -ENOMEM; + + dev_set_drvdata(&pdev->dev, bt_bmc); + + bt_bmc->map = syscon_node_to_regmap(pdev->dev.parent->of_node); + + mutex_init(&bt_bmc->mutex); + init_waitqueue_head(&bt_bmc->queue); + + bt_bmc->miscdev.minor = MISC_DYNAMIC_MINOR, + bt_bmc->miscdev.name = DEVICE_NAME, + bt_bmc->miscdev.fops = &bt_bmc_fops, + bt_bmc->miscdev.parent = dev; + rc = misc_register(&bt_bmc->miscdev); + if (rc) { + dev_err(dev, "Unable to register misc device\n"); + return rc; + } + + bt_bmc_config_irq(bt_bmc, pdev); + + if (bt_bmc->irq) { + dev_info(dev, "Using IRQ %d\n", bt_bmc->irq); + } else { + dev_info(dev, "No IRQ; using timer\n"); + timer_setup(&bt_bmc->poll_timer, poll_timer, 0); + bt_bmc->poll_timer.expires = jiffies + msecs_to_jiffies(10); + add_timer(&bt_bmc->poll_timer); + } + + regmap_update_bits(bt_bmc->map, LPC_HICR0, LPC_HICR0_LPC3E, LPC_HICR0_LPC3E); + regmap_update_bits(bt_bmc->map, LPC_HICR4, LPC_BTENABLE, LPC_BTENABLE); + regmap_write(bt_bmc->map, LPC_LADR3H_BT, BT_IO_BASE >> 8); + regmap_write(bt_bmc->map, LPC_LADR3L_BT, BT_IO_BASE); + regmap_update_bits(bt_bmc->map, LPC_HICR2, LPC_HICR2_IBFIF3, LPC_HICR2_IBFIF3); + clr_b_busy(bt_bmc); + + return 0; +} + +static int bt_bmc_remove(struct platform_device *pdev) +{ + struct bt_bmc *bt_bmc = dev_get_drvdata(&pdev->dev); + + misc_deregister(&bt_bmc->miscdev); + if (!bt_bmc->irq) + del_timer_sync(&bt_bmc->poll_timer); + + return 0; +} + +static const struct of_device_id bt_bmc_match[] = { + { .compatible = "phytium,bt-bmc" }, + { }, +}; +MODULE_DEVICE_TABLE(of, bt_bmc_match); + +static struct platform_driver bt_bmc_driver = { + .driver = { + .name = DEVICE_NAME, + .of_match_table = bt_bmc_match, + }, + .probe = bt_bmc_probe, + .remove = bt_bmc_remove, +}; + +module_platform_driver(bt_bmc_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Cheng Quan +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kcs_bmc.h" + +#define DEVICE_NAME "phytium-kcs-bmc" + +#define KCS_CHANNEL_MAX 4 + +/* mapped to lpc-bmc@0 IO space */ +#define LPC_HICR0 0x000 +#define LPC_HICR0_LPC3E BIT(7) +#define LPC_HICR0_LPC2E BIT(6) +#define LPC_HICR0_LPC1E BIT(5) +#define LPC_HICR0_SDWNE BIT(3) /* 0:disable, 1:enable for HICR1_SDWNB */ +#define LPC_HICR0_PMEE BIT(2) /* 0:disable, 1:enable for HICR1_PMEB */ +#define LPC_HICR1 0x004 +#define LPC_HICR1_LPCBSY BIT(7) /* 0:idle, 1:trans busy */ +#define LPC_HICR1_IRQBSY BIT(5) /* 0:idle, 1:trans data */ +#define LPC_HICR1_LPCSWRST BIT(4) /* 0:normal, 1:reset */ +#define LPC_HICR1_SDWNB BIT(3) /* 0:normal, 1:software shutdown */ +#define LPC_HICR1_PMEB BIT(2) /* 0:LPCPD low, 1:LPCPD high */ +#define LPC_HICR2 0x008 +#define LPC_LPCSWRST_ERRIRQ BIT(6) /* 0:normal, 1:reset irq */ +#define LPC_SDWN_ERRIRQ BIT(5) /* 0:normal, 1:lpcpd irq */ +#define LPC_ABRT_ERRIRQ BIT(4) /* 0:normal, 1:lframe low when busy*/ +#define LPC_HICR2_IBFIF3 BIT(3) /* 0:normal, 1:enable irq 3*/ +#define LPC_HICR2_IBFIF2 BIT(2) +#define LPC_HICR2_IBFIF1 BIT(1) +/* 0:normal,1:enable err irq-reset,power down,abort */ +#define LPC_HICR2_ERRIE BIT(0) +#define LPC_HICR3 0x00C +#define LPC_LFRAME_STATUS BIT(7) /* R */ +#define LPC_SERIRQ_STATUS BIT(5) /* R */ +#define LPC_LREST_STATUS BIT(4) /* R */ +#define LPC_LPCPD_STATUS BIT(3) /* R */ +#define LPC_PME_STATUS BIT(2) /* R */ +#define LPC_HICR4 0x010 +#define LPC_HICR4_LADR12AS BIT(7) +#define LPC_HICR4_KCSENBL BIT(2) +#define LPC_BTENABLE BIT(0) /* share bt channel enable, 0:disable, 1:enable */ +#define LPC_LADR3H 0x014 +#define LPC_LADR3L 0x018 +#define LPC_LADR12H 0x01C +#define LPC_LADR12L 0x020 +#define LPC_IDR1 0x024 +#define LPC_IDR2 0x028 +#define LPC_IDR3 0x02C +#define LPC_ODR1 0x030 +#define LPC_ODR2 0x034 +#define LPC_ODR3 0x038 +#define LPC_STR1 0x03C +#define LPC_STR2 0x040 +#define LPC_STR3 0x044 + +#define LPC_HICRB 0x80 +#define LPC_HICRB_IBFIF4 BIT(1) +#define LPC_HICRB_LPC4E BIT(0) +#define LPC_LADR4 0x88 +#define LPC_IDR4 0x8c +#define LPC_ODR4 0x90 +#define LPC_STR4 0x94 + +struct phytium_kcs_bmc { + struct regmap *map; +}; + +static u8 phytium_kcs_inb(struct kcs_bmc *kcs_bmc, u32 reg) +{ + struct phytium_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc); + u32 val = 0; + int rc; + + rc = regmap_read(priv->map, reg, &val); + WARN(rc != 0, "regmap_read() failed: %d\n", rc); + + return rc == 0 ? (u8) val : 0; +} + +static void phytium_kcs_outb(struct kcs_bmc *kcs_bmc, u32 reg, u8 data) +{ + struct phytium_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc); + int rc; + + rc = regmap_write(priv->map, reg, data); + WARN(rc != 0, "regmap_write() failed: %d\n", rc); +} + +/* + * Background: + * we note D for Data, and C for Cmd/Status, default rules are + * A. KCS1 / KCS2 ( D / C:X / X+4 ) + * D / C : CA0h / CA4h + * D / C : CA8h / CACh + * B. KCS3 ( D / C:XX2h / XX3h ) + * D / C : CA2h / CA3h + * D / C : CB2h / CB3h -use + * C. KCS4 + * D / C : CA4h / CA5h + * D / C : CB0h / CB1h -use + */ +static void phytium_kcs_set_address(struct kcs_bmc *kcs_bmc, u16 addr) +{ + struct phytium_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc); + + switch (kcs_bmc->channel) { + case 1: + regmap_update_bits(priv->map, LPC_HICR4, LPC_HICR4_LADR12AS, 0); + regmap_write(priv->map, LPC_LADR12H, addr >> 8); + regmap_write(priv->map, LPC_LADR12L, addr & 0xFF); + break; + case 2: + regmap_update_bits(priv->map, LPC_HICR4, + LPC_HICR4_LADR12AS, LPC_HICR4_LADR12AS); + regmap_write(priv->map, LPC_LADR12H, addr >> 8); + regmap_write(priv->map, LPC_LADR12L, addr & 0xFF); + break; + case 3: + regmap_write(priv->map, LPC_LADR3H, addr >> 8); + regmap_write(priv->map, LPC_LADR3L, addr & 0xFF); + break; + case 4: + regmap_write(priv->map, LPC_LADR4, ((addr + 1) << 16) | + addr); + break; + default: + break; + } +} + +static void phytium_kcs_enable_channel(struct kcs_bmc *kcs_bmc, bool enable) +{ + struct phytium_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc); + + switch (kcs_bmc->channel) { + case 1: + if (enable) { + regmap_update_bits(priv->map, LPC_HICR2, + LPC_HICR2_IBFIF1, LPC_HICR2_IBFIF1); + regmap_update_bits(priv->map, LPC_HICR0, + LPC_HICR0_LPC1E, LPC_HICR0_LPC1E); + } else { + regmap_update_bits(priv->map, LPC_HICR0, + LPC_HICR0_LPC1E, 0); + regmap_update_bits(priv->map, LPC_HICR2, + LPC_HICR2_IBFIF1, 0); + } + break; + case 2: + if (enable) { + regmap_update_bits(priv->map, LPC_HICR2, + LPC_HICR2_IBFIF2, LPC_HICR2_IBFIF2); + regmap_update_bits(priv->map, LPC_HICR0, + LPC_HICR0_LPC2E, LPC_HICR0_LPC2E); + } else { + regmap_update_bits(priv->map, LPC_HICR0, + LPC_HICR0_LPC2E, 0); + regmap_update_bits(priv->map, LPC_HICR2, + LPC_HICR2_IBFIF2, 0); + } + break; + case 3: + if (enable) { + regmap_update_bits(priv->map, LPC_HICR2, + LPC_HICR2_IBFIF3, LPC_HICR2_IBFIF3); + regmap_update_bits(priv->map, LPC_HICR0, + LPC_HICR0_LPC3E, LPC_HICR0_LPC3E); + regmap_update_bits(priv->map, LPC_HICR4, + LPC_HICR4_KCSENBL, LPC_HICR4_KCSENBL); + } else { + regmap_update_bits(priv->map, LPC_HICR0, + LPC_HICR0_LPC3E, 0); + regmap_update_bits(priv->map, LPC_HICR4, + LPC_HICR4_KCSENBL, 0); + regmap_update_bits(priv->map, LPC_HICR2, + LPC_HICR2_IBFIF3, 0); + } + break; + case 4: + if (enable) + regmap_update_bits(priv->map, LPC_HICRB, + LPC_HICRB_IBFIF4 | LPC_HICRB_LPC4E, + LPC_HICRB_IBFIF4 | LPC_HICRB_LPC4E); + else + regmap_update_bits(priv->map, LPC_HICRB, + LPC_HICRB_IBFIF4 | LPC_HICRB_LPC4E, + 0); + break; + default: + break; + } +} + +static irqreturn_t phytium_kcs_irq(int irq, void *arg) +{ + struct kcs_bmc *kcs_bmc = arg; + + if (!kcs_bmc_handle_event(kcs_bmc)) + return IRQ_HANDLED; + + return IRQ_NONE; +} + +static int phytium_kcs_config_irq(struct kcs_bmc *kcs_bmc, struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + int irq; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + return devm_request_irq(dev, irq, phytium_kcs_irq, IRQF_SHARED, + dev_name(dev), kcs_bmc); +} + +static const struct kcs_ioreg phytium_kcs_bmc_ioregs[KCS_CHANNEL_MAX] = { + { .idr = LPC_IDR1, .odr = LPC_ODR1, .str = LPC_STR1 }, + { .idr = LPC_IDR2, .odr = LPC_ODR2, .str = LPC_STR2 }, + { .idr = LPC_IDR3, .odr = LPC_ODR3, .str = LPC_STR3 }, + { .idr = LPC_IDR4, .odr = LPC_ODR4, .str = LPC_STR4 }, +}; + +static int phytium_kcs_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct phytium_kcs_bmc *priv; + struct kcs_bmc *kcs_bmc; + u32 chan, addr; + int rc; + + rc = of_property_read_u32(dev->of_node, "kcs_chan", &chan); + if ((rc != 0) || (chan == 0 || chan > KCS_CHANNEL_MAX)) { + dev_err(dev, "no valid 'kcs_chan' configured\n"); + return -ENODEV; + } + + rc = of_property_read_u32(dev->of_node, "kcs_addr", &addr); + if (rc) { + dev_err(dev, "no valid 'kcs_addr' configured\n"); + return -ENODEV; + } + + kcs_bmc = kcs_bmc_alloc(dev, sizeof(*priv), chan); + if (!kcs_bmc) + return -ENOMEM; + + priv = kcs_bmc_priv(kcs_bmc); + priv->map = syscon_node_to_regmap(dev->parent->of_node); + if (IS_ERR(priv->map)) { + dev_err(dev, "Couldn't get regmap\n"); + return -ENODEV; + } + + kcs_bmc->ioreg = phytium_kcs_bmc_ioregs[chan - 1]; + kcs_bmc->io_inputb = phytium_kcs_inb; + kcs_bmc->io_outputb = phytium_kcs_outb; + + dev_set_drvdata(dev, kcs_bmc); + + phytium_kcs_set_address(kcs_bmc, addr); + phytium_kcs_enable_channel(kcs_bmc, true); + rc = phytium_kcs_config_irq(kcs_bmc, pdev); + if (rc) + return rc; + + rc = misc_register(&kcs_bmc->miscdev); + if (rc) { + dev_err(dev, "Unable to register device\n"); + return rc; + } + + pr_info("channel=%u addr=0x%x idr=0x%x odr=0x%x str=0x%x\n", + chan, addr, kcs_bmc->ioreg.idr, kcs_bmc->ioreg.odr, kcs_bmc->ioreg.str); + + return 0; +} + +static int phytium_kcs_remove(struct platform_device *pdev) +{ + struct kcs_bmc *kcs_bmc = dev_get_drvdata(&pdev->dev); + + misc_deregister(&kcs_bmc->miscdev); + + return 0; +} + +static const struct of_device_id phytium_kcs_bmc_match[] = { + { .compatible = "phytium,kcs-bmc" }, + { } +}; +MODULE_DEVICE_TABLE(of, phytium_kcs_bmc_match); + +static struct platform_driver phytium_kcs_bmc_driver = { + .driver = { + .name = DEVICE_NAME, + .of_match_table = phytium_kcs_bmc_match, + }, + .probe = phytium_kcs_probe, + .remove = phytium_kcs_remove, +}; +module_platform_driver(phytium_kcs_bmc_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Cheng Quan "); +MODULE_DESCRIPTION("Phytium device interface to the KCS BMC device"); diff --git a/target/linux/phytium/files-5.10/drivers/dma/phytium/Makefile b/target/linux/phytium/files-5.10/drivers/dma/phytium/Makefile new file mode 100644 index 000000000..71ba9b9fc --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/dma/phytium/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_PHYTIUM_DDMA) += phytium-ddmac.o diff --git a/target/linux/phytium/files-5.10/drivers/dma/phytium/phytium-ddmac.c b/target/linux/phytium/files-5.10/drivers/dma/phytium/phytium-ddmac.c new file mode 100644 index 000000000..9ff66bdc9 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/dma/phytium/phytium-ddmac.c @@ -0,0 +1,945 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Phytium Device DDMA Controller driver. + * + * Copyright (c) 2023 Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "phytium-ddmac.h" + + +static inline struct phytium_ddma_device *to_ddma_device(struct dma_chan *chan) +{ + return container_of(chan->device, struct phytium_ddma_device, dma_dev); +} + +static inline struct phytium_ddma_chan *to_ddma_chan(struct dma_chan *chan) +{ + return container_of(chan, struct phytium_ddma_chan, vchan.chan); +} + +static inline struct phytium_ddma_desc *to_ddma_desc(struct virt_dma_desc *vd) +{ + return container_of(vd, struct phytium_ddma_desc, vdesc); +} + +static inline struct device *chan_to_dev(struct phytium_ddma_chan *chan) +{ + return chan->vchan.chan.device->dev; +} + +static inline struct phytium_ddma_device *chan_to_ddma( + struct phytium_ddma_chan *chan) +{ + return to_ddma_device(&chan->vchan.chan); +} + +static inline void phytium_ddma_iowrite32( + const struct phytium_ddma_device *ddma, + const u32 reg, const u32 val) +{ + iowrite32(val, ddma->base + reg); +} + +static inline u32 phytium_ddma_ioread32(const struct phytium_ddma_device *ddma, + const u32 reg) +{ + return ioread32(ddma->base + reg); +} + +static inline void phytium_chan_iowrite32(const struct phytium_ddma_chan *chan, + const u32 reg, const u32 val) +{ + iowrite32(val, chan->base + reg); +} + +static inline u32 phytium_chan_ioread32(const struct phytium_ddma_chan *chan, + const u32 reg) +{ + return ioread32(chan->base + reg); +} + +static void phytium_ddma_disable(const struct phytium_ddma_device *ddma) +{ + dev_dbg(ddma->dev, "ddma disable\n"); + phytium_ddma_iowrite32(ddma, DMA_CTL, !DMA_CTL_EN); +} + +static void phytium_ddma_enable(const struct phytium_ddma_device *ddma) +{ + dev_dbg(ddma->dev, "ddma enable\n"); + phytium_ddma_iowrite32(ddma, DMA_CTL, DMA_CTL_EN); +} + +static void phytium_ddma_reset(const struct phytium_ddma_device *ddma) +{ + u32 val = 0; + + dev_dbg(ddma->dev, "dma reset\n"); + val = phytium_ddma_ioread32(ddma, DMA_CTL); + val |= DMA_CTL_SRST; + phytium_ddma_iowrite32(ddma, DMA_CTL, val); + + udelay(10); + val &= ~DMA_CTL_SRST; + phytium_ddma_iowrite32(ddma, DMA_CTL, val); +} + +static void phytium_ddma_irq_disable(const struct phytium_ddma_device *ddma) +{ + u32 val = 0; + + dev_dbg(ddma->dev, "ddma irq disable\n"); + val = phytium_ddma_ioread32(ddma, DMA_MASK_INT); + val |= DMA_INT_EN; + phytium_ddma_iowrite32(ddma, DMA_MASK_INT, val); +} + +static void phytium_ddma_irq_enable(const struct phytium_ddma_device *ddma) +{ + u32 val = 0; + + dev_dbg(ddma->dev, "ddma irq enable\n"); + val = phytium_ddma_ioread32(ddma, DMA_MASK_INT); + val &= ~DMA_INT_EN; + phytium_ddma_iowrite32(ddma, DMA_MASK_INT, val); +} + +static u32 phytium_ddma_irq_read(const struct phytium_ddma_device *ddma) +{ + u32 val = 0; + + val = phytium_ddma_ioread32(ddma, DMA_STAT); + + return val; +} + +static void phytium_chan_irq_disable(struct phytium_ddma_chan *chan) +{ + u32 val = 0; + + dev_dbg(chan_to_dev(chan), "channel %d irq disable\n", chan->id); + val = phytium_ddma_ioread32(chan_to_ddma(chan), DMA_MASK_INT); + val |= DMA_INT_CHAL_EN(chan->id); + phytium_ddma_iowrite32(chan_to_ddma(chan), DMA_MASK_INT, val); +} + +static void phytium_chan_irq_enable(struct phytium_ddma_chan *chan) +{ + u32 val = 0; + + dev_dbg(chan_to_dev(chan), "channel %d irq enable\n", chan->id); + val = phytium_ddma_ioread32(chan_to_ddma(chan), DMA_MASK_INT); + val &= ~DMA_INT_CHAL_EN(chan->id); + phytium_ddma_iowrite32(chan_to_ddma(chan), DMA_MASK_INT, val); +} + +static void phytium_chan_irq_clear(struct phytium_ddma_chan *chan) +{ + u32 val = 0; + + dev_dbg(chan_to_dev(chan), "channel %d irq clear\n", chan->id); + val = DMA_STAT_CHAL(chan->id); + phytium_ddma_iowrite32(chan_to_ddma(chan), DMA_STAT, val); +} + +static int phytium_chan_disable(struct phytium_ddma_chan *chan) +{ + u32 val = 0; + int ret = 0; + + dev_dbg(chan_to_dev(chan), "channel %d disable\n", chan->id); + val = phytium_chan_ioread32(chan, DMA_CHALX_CTL); + if (val | DMA_CHAL_EN) { + val &= ~DMA_CHAL_EN; + phytium_chan_iowrite32(chan, DMA_CHALX_CTL, val); + + ret = readl_relaxed_poll_timeout_atomic( + chan->base + DMA_CHALX_CTL, val, + !(val & DMA_CHAL_EN), 0, 100000); + } + return ret; +} + +static void phytium_chan_enable(struct phytium_ddma_chan *chan) +{ + u32 val = 0; + + dev_dbg(chan_to_dev(chan), "channel %d enable\n", chan->id); + val = phytium_chan_ioread32(chan, DMA_CHALX_CTL); + val |= DMA_CHAL_EN; + phytium_chan_iowrite32(chan, DMA_CHALX_CTL, val); +} + +static bool phytium_chan_is_running(const struct phytium_ddma_chan *chan) +{ + u32 val; + + val = phytium_chan_ioread32(chan, DMA_CHALX_CTL); + + if (val & DMA_CHAL_EN) + return true; + else + return false; +} + +static void phytium_chan_reset(struct phytium_ddma_chan *chan) +{ + u32 val = 0; + + dev_dbg(chan_to_dev(chan), "channel %d reset\n", chan->id); + val = phytium_chan_ioread32(chan, DMA_CHALX_CTL); + val |= DMA_CHAL_SRST; + phytium_chan_iowrite32(chan, DMA_CHALX_CTL, val); + + udelay(10); + val &= ~DMA_CHAL_SRST; + phytium_chan_iowrite32(chan, DMA_CHALX_CTL, val); +} + +static void phytium_ddma_vdesc_free(struct virt_dma_desc *vd) +{ + kfree(to_ddma_desc(vd)); +} + +static int phytium_chan_pause(struct dma_chan *chan) +{ + struct phytium_ddma_chan *pchan = to_ddma_chan(chan); + int ret = 0; + + ret = phytium_chan_disable(pchan); + pchan->busy = false; + pchan->is_pasued = true; + + return ret; +} + +static int phytium_chan_resume(struct dma_chan *chan) +{ + struct phytium_ddma_chan *pchan = to_ddma_chan(chan); + + phytium_chan_enable(pchan); + pchan->is_pasued = false; + + return 0; +} + +static void phytium_chan_start_xfer(struct phytium_ddma_chan *chan) +{ + struct virt_dma_desc *vdesc = NULL; + struct phytium_ddma_sg_req *sg_req = NULL; + char *tmp = NULL; + int i = 0; + unsigned long flags = 0; + + /* chan first xfer settings */ + if (!chan->desc) { + vdesc = vchan_next_desc(&chan->vchan); + if (!vdesc) + return; + + list_del(&vdesc->node); + chan->desc = to_ddma_desc(vdesc); + chan->next_sg = 0; + chan->current_sg = NULL; + dev_dbg(chan_to_dev(chan), "xfer start\n"); + } + + if (chan->next_sg == chan->desc->num_sgs) + chan->next_sg = 0; + + sg_req = &chan->desc->sg_req[chan->next_sg]; + chan->current_sg = sg_req; + /* fill to 4 bytes */ + switch (sg_req->direction) { + case DMA_MEM_TO_DEV: + tmp = phys_to_virt(sg_req->mem_addr_l); + memset(chan->buf, 0, sg_req->len * 4); + for (i = 0; i < sg_req->len; i++) + chan->buf[i * 4] = tmp[i]; + break; + + case DMA_DEV_TO_MEM: + memset(chan->buf, 0, sg_req->len * 4); + break; + + default: + break; + } + + /* start transfer */ + phytium_chan_iowrite32(chan, DMA_CHALX_DDR_LWADDR, + chan->paddr & 0xFFFFFFFF); + phytium_chan_iowrite32(chan, DMA_CHALX_DDR_UPADDR, + (chan->paddr >> 32) & 0xFFFFFFFF); + phytium_chan_iowrite32(chan, DMA_CHALX_DEV_ADDR, sg_req->dev_addr); + phytium_chan_iowrite32(chan, DMA_CHALX_TS, sg_req->len * 4); + + spin_lock_irqsave(&chan_to_ddma(chan)->lock, flags); + phytium_chan_irq_enable(chan); + spin_unlock_irqrestore(&chan_to_ddma(chan)->lock, flags); + phytium_chan_enable(chan); + + chan->next_sg++; + chan->busy = true; +} + +static void phytium_chan_xfer_done(struct phytium_ddma_chan *chan) +{ + struct phytium_ddma_sg_req *sg_req = chan->current_sg; + char *tmp = NULL; + int i = 0; + + if (chan->desc) { + if (sg_req->direction == DMA_DEV_TO_MEM) { + tmp = phys_to_virt(sg_req->mem_addr_l); + for (i = 0; i < sg_req->len; i++) + tmp[i] = chan->buf[i * 4]; + } + + chan->busy = false; + if (chan->next_sg == chan->desc->num_sgs) { + dev_dbg(chan_to_dev(chan), "xfer complete\n"); + vchan_cookie_complete(&chan->desc->vdesc); + chan->desc = NULL; + chan->current_sg = NULL; + } + phytium_chan_disable(chan); + phytium_chan_irq_clear(chan); + phytium_chan_start_xfer(chan); + } +} + +static void phytium_dma_hw_init(struct phytium_ddma_device *ddma) +{ + u32 i = 0; + int ret = 0; + + phytium_ddma_disable(ddma); + phytium_ddma_reset(ddma); + phytium_ddma_irq_enable(ddma); + phytium_ddma_enable(ddma); + + for (i = 0; i < ddma->dma_channels; i++) { + phytium_chan_irq_disable(&ddma->chan[i]); + ret = phytium_chan_disable(&ddma->chan[i]); + if (ret) + dev_err(ddma->dev, "can't disable channel %d\n", i); + } +} + +static size_t phytium_ddma_desc_residue(struct phytium_ddma_chan *chan) +{ + u32 trans_cnt = 0; + u32 residue = 0; + int i = 0; + + trans_cnt = phytium_chan_ioread32(chan, DMA_CHALX_TRANS_CNT); + residue = chan->current_sg->len - trans_cnt; + + for (i = chan->next_sg; i < chan->desc->num_sgs; i++) + residue += chan->desc->sg_req[i].len; + + return residue; +} + +static enum dma_status phytium_ddma_tx_status(struct dma_chan *chan, + dma_cookie_t cookie, + struct dma_tx_state *txstate) +{ + struct phytium_ddma_chan *pchan = to_ddma_chan(chan); + struct virt_dma_desc *vd = NULL; + enum dma_status ret = 0; + unsigned long flags = 0; + size_t residue = 0; + + ret = dma_cookie_status(chan, cookie, txstate); + if ((ret == DMA_COMPLETE) || !txstate) + return ret; + + spin_lock_irqsave(&pchan->vchan.lock, flags); + vd = vchan_find_desc(&pchan->vchan, cookie); + if (pchan->desc && cookie == pchan->desc->vdesc.tx.cookie) + residue = phytium_ddma_desc_residue(pchan); + + dma_set_residue(txstate, residue); + spin_unlock_irqrestore(&pchan->vchan.lock, flags); + + if (pchan->is_pasued && ret == DMA_IN_PROGRESS) + ret = DMA_PAUSED; + + return ret; +} + +static void phytium_ddma_issue_pending(struct dma_chan *chan) +{ + struct phytium_ddma_chan *pchan = to_ddma_chan(chan); + unsigned long flags = 0; + + spin_lock_irqsave(&pchan->vchan.lock, flags); + + if (vchan_issue_pending(&pchan->vchan) && !pchan->desc && !pchan->busy) + phytium_chan_start_xfer(pchan); + + spin_unlock_irqrestore(&pchan->vchan.lock, flags); +} + +static int phytium_ddma_terminate_all(struct dma_chan *chan) +{ + struct phytium_ddma_chan *pchan = to_ddma_chan(chan); + unsigned long flags = 0; + LIST_HEAD(head); + + spin_lock_irqsave(&pchan->vchan.lock, flags); + if (pchan->desc) { + vchan_terminate_vdesc(&pchan->desc->vdesc); + if (pchan->busy) { + u32 tmp_ctl, timeout; + phytium_chan_disable(pchan); + /* save some registers, reset will clear it */ + timeout = phytium_chan_ioread32(pchan, + DMA_CHALX_TIMEOUT_CNT); + tmp_ctl = phytium_chan_ioread32(pchan, + DMA_CHALX_CTL); + spin_lock(&chan_to_ddma(pchan)->lock); + phytium_chan_irq_disable(pchan); + spin_unlock(&chan_to_ddma(pchan)->lock); + /* need reset when terminate */ + phytium_chan_reset(pchan); + phytium_chan_irq_clear(pchan); + /* recover it */ + phytium_chan_iowrite32(pchan, + DMA_CHALX_CTL, tmp_ctl); + phytium_chan_iowrite32(pchan, + DMA_CHALX_TIMEOUT_CNT, timeout); + pchan->busy = false; + } + pchan->desc = NULL; + } + + vchan_get_all_descriptors(&pchan->vchan, &head); + spin_unlock_irqrestore(&pchan->vchan.lock, flags); + vchan_dma_desc_free_list(&pchan->vchan, &head); + + return 0; +} + +static int phytium_ddma_alloc_chan_resources(struct dma_chan *chan) +{ + struct phytium_ddma_device *ddma = to_ddma_device(chan); + struct phytium_ddma_chan *pchan = to_ddma_chan(chan); + u32 bind_status = 0; + int ret = 0; + unsigned long flags = 0; + + bind_status = phytium_ddma_ioread32(ddma, DMA_CHAL_BIND); + + if ((pchan->is_used) || (bind_status & BIT(pchan->id))) { + dev_err(ddma->dev, "channel %d already used\n", pchan->id); + ret = -EBUSY; + goto out; + } + + /* prepare channel */ + ret = phytium_chan_disable(pchan); + if (ret) { + dev_err(ddma->dev, "can't disable channel %d\n", pchan->id); + goto out; + } + phytium_chan_reset(pchan); + phytium_chan_irq_clear(pchan); + + /* channel bind */ + spin_lock_irqsave(&chan_to_ddma(pchan)->lock, flags); + bind_status |= BIT(pchan->id); + phytium_ddma_iowrite32(ddma, DMA_CHAL_BIND, bind_status); + pchan->is_used = true; + spin_unlock_irqrestore(&chan_to_ddma(pchan)->lock, flags); + + /* alloc dma memory */ + pchan->buf = dma_alloc_coherent(ddma->dev, 4 * PAGE_SIZE, &pchan->paddr, + GFP_KERNEL); + if (!pchan->buf) { + ret = -EBUSY; + dev_err(ddma->dev, "failed to alloc dma memory\n"); + } + + dev_info(ddma->dev, "alloc channel %d\n", pchan->id); + +out: + return ret; +} + +static void phytium_ddma_free_chan_resources(struct dma_chan *chan) +{ + struct phytium_ddma_device *ddma = to_ddma_device(chan); + struct phytium_ddma_chan *pchan = to_ddma_chan(chan); + u32 bind_status = 0; + unsigned long flags = 0; + + if (!pchan->is_used) + return; + + dev_dbg(ddma->dev, "free channel %d\n", pchan->id); + spin_lock_irqsave(&chan_to_ddma(pchan)->lock, flags); + bind_status = phytium_ddma_ioread32(ddma, DMA_CHAL_BIND); + bind_status &= ~BIT(pchan->id); + phytium_ddma_iowrite32(ddma, DMA_CHAL_BIND, bind_status); + spin_unlock_irqrestore(&chan_to_ddma(pchan)->lock, flags); + + phytium_chan_disable(pchan); + + spin_lock_irqsave(&chan_to_ddma(pchan)->lock, flags); + phytium_chan_irq_disable(pchan); + spin_unlock_irqrestore(&chan_to_ddma(pchan)->lock, flags); + + vchan_free_chan_resources(to_virt_chan(chan)); + pchan->is_used = false; + + if (pchan->buf) + dma_free_coherent(ddma->dev, 4 * PAGE_SIZE, + pchan->buf, pchan->paddr); +} + +static int phytium_ddma_slave_config(struct dma_chan *chan, + struct dma_slave_config *config) +{ + struct phytium_ddma_chan *pchan = to_ddma_chan(chan); + u32 chal_cfg = 0; + u32 req_mode = 0; + const u32 timeout = 0xffff; + unsigned long flag = 0; + + /* Check if chan will be configured for slave transfers */ + if (!is_slave_direction(config->direction)) + return -EINVAL; + + memcpy(&pchan->dma_config, config, sizeof(*config)); + + /* set channel config reg */ + spin_lock_irqsave(&chan_to_ddma(pchan)->lock, flag); + if (pchan->id > 3) { + chal_cfg = phytium_ddma_ioread32(chan_to_ddma(pchan), + DMA_CHAL_CFG_H); + chal_cfg &= ~(0xFF << ((pchan->id - 4) * 8)); + chal_cfg |= DMA_CHAL_SEL((pchan->id - 4), pchan->request_line); + chal_cfg |= DMA_CHAL_SEL_EN(pchan->id - 4); + phytium_ddma_iowrite32(chan_to_ddma(pchan), DMA_CHAL_CFG_H, + chal_cfg); + } else { + chal_cfg = phytium_ddma_ioread32(chan_to_ddma(pchan), + DMA_CHAL_CFG_L); + chal_cfg &= ~(0xFF << (pchan->id * 8)); + chal_cfg |= DMA_CHAL_SEL((pchan->id), pchan->request_line); + chal_cfg |= DMA_CHAL_SEL_EN(pchan->id); + phytium_ddma_iowrite32(chan_to_ddma(pchan), DMA_CHAL_CFG_L, + chal_cfg); + } + spin_unlock_irqrestore(&chan_to_ddma(pchan)->lock, flag); + + /* set channel mode */ + req_mode = (config->direction == DMA_DEV_TO_MEM) ? + DMA_RX_REQ : DMA_TX_REQ; + phytium_chan_iowrite32(pchan, DMA_CHALX_CTL, req_mode << 2); + + /* set channel timeout */ + phytium_chan_iowrite32(pchan, DMA_CHALX_TIMEOUT_CNT, + timeout | DMA_CHAL_TIMEOUT_EN); + + return 0; +} + +static struct dma_async_tx_descriptor *phytium_ddma_prep_slave_sg( + struct dma_chan *chan, struct scatterlist *sgl, + u32 sg_len, enum dma_transfer_direction direction, + unsigned long flags, void *context) +{ + struct phytium_ddma_device *ddma = to_ddma_device(chan); + struct phytium_ddma_chan *pchan = to_ddma_chan(chan); + struct dma_slave_config *sconfig = &pchan->dma_config; + struct phytium_ddma_desc *desc = NULL; + struct scatterlist *sg = NULL; + int i = 0; + char *tmp; + + if (unlikely(!is_slave_direction(direction))) { + dev_err(ddma->dev, "invalid dma direction\n"); + return NULL; + } + + if (unlikely(sg_len < 1)) { + dev_err(ddma->dev, "invalid segment length: %d\n", sg_len); + return NULL; + } + + desc = kzalloc(struct_size(desc, sg_req, sg_len), GFP_NOWAIT); + if (!desc) + return NULL; + + /* set sg list */ + for_each_sg(sgl, sg, sg_len, i) { + tmp = phys_to_virt(sg_dma_address(sg)); + desc->sg_req[i].direction = direction; + + switch (direction) { + case DMA_MEM_TO_DEV: + desc->sg_req[i].len = sg_dma_len(sg); + desc->sg_req[i].mem_addr_l = + sg_dma_address(sg) & 0xFFFFFFFF; + desc->sg_req[i].mem_addr_h = + (sg_dma_address(sg) >> 32) & 0xFFFFFFFF; + desc->sg_req[i].dev_addr = + sconfig->dst_addr & 0xFFFFFFFF; + break; + + case DMA_DEV_TO_MEM: + desc->sg_req[i].len = sg_dma_len(sg); + desc->sg_req[i].mem_addr_l = + sg_dma_address(sg) & 0xFFFFFFFF; + desc->sg_req[i].mem_addr_h = + (sg_dma_address(sg) >> 32) & 0xFFFFFFFF; + desc->sg_req[i].dev_addr = + sconfig->src_addr & 0xFFFFFFFF; + break; + + default: + return NULL; + } + } + + desc->num_sgs = sg_len; + + return vchan_tx_prep(&pchan->vchan, &desc->vdesc, flags); +} + +static irqreturn_t phytium_dma_interrupt(int irq, void *dev_id) +{ + struct phytium_ddma_device *ddma = dev_id; + struct phytium_ddma_chan *chan; + u32 irq_status = 0; + u32 i = 0; + u32 val = 0; + + phytium_ddma_irq_disable(ddma); + + irq_status = phytium_ddma_irq_read(ddma); + val = phytium_ddma_ioread32(ddma, DMA_CTL); + + /* Poll, clear and process every chanel interrupt status */ + for (i = 0; i < ddma->dma_channels; i++) { + if (!(irq_status & BIT(i * 4))) + continue; + + chan = &ddma->chan[i]; + phytium_chan_xfer_done(chan); + } + + phytium_ddma_irq_enable(ddma); + + return IRQ_HANDLED; +} + + +static struct dma_chan *phytium_ddma_of_xlate(struct of_phandle_args *dma_spec, + struct of_dma *ofdma) +{ + struct phytium_ddma_device *ddma = ofdma->of_dma_data; + struct device *dev = ddma->dev; + struct phytium_ddma_chan *chan = NULL; + struct dma_chan *c = NULL; + u32 channel_id = 0; + + channel_id = dma_spec->args[0]; + + if (channel_id > ddma->dma_channels) { + dev_err(dev, "bad channel %d\n", channel_id); + return NULL; + } + + chan = &ddma->chan[channel_id]; + chan->request_line = dma_spec->args[1]; + c = dma_get_slave_channel(&chan->vchan.chan); + if (!c) { + dev_err(dev, "no more channels available\n"); + return NULL; + } + + return c; +} + +static int phytium_ddma_probe(struct platform_device *pdev) +{ + struct phytium_ddma_device *ddma; + struct dma_device *dma_dev; + struct resource *mem; + u32 i = 0; + int ret = 0; + u32 nr_channels = 0; + + ddma = devm_kzalloc(&pdev->dev, sizeof(*ddma), GFP_KERNEL); + if (!ddma) { + ret = -ENOMEM; + goto out; + } + + dma_dev = &ddma->dma_dev; + ddma->dev = &pdev->dev; + + spin_lock_init(&ddma->lock); + + ddma->irq = platform_get_irq(pdev, 0); + if (ddma->irq < 0) { + dev_err(&pdev->dev, "no irq resource\n"); + ret = -EINVAL; + goto out; + } + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ddma->base = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(ddma->base)) { + dev_err(&pdev->dev, "no resource address"); + ret = PTR_ERR(ddma->base); + goto out; + } + + ret = of_property_read_u32(pdev->dev.of_node, "dma-channels", + &nr_channels); + if (ret < 0) { + dev_err(&pdev->dev, + "can't get the number of dma channels: %d\n", ret); + goto out; + } + + if (nr_channels > DDMA_MAX_NR_PCHANNELS) { + dev_warn(&pdev->dev, "over the max number of channels\n"); + nr_channels = DDMA_MAX_NR_PCHANNELS; + } + + ddma->dma_channels = DDMA_MAX_NR_PCHANNELS; + + ret = devm_request_irq(&pdev->dev, ddma->irq, phytium_dma_interrupt, + IRQF_SHARED, dev_name(&pdev->dev), ddma); + if (ret) { + dev_err(&pdev->dev, "could not to request irq %d", ddma->irq); + goto out; + } + + /* Set capabilities */ + dma_cap_set(DMA_SLAVE, ddma->dma_dev.cap_mask); + + /* DMA capabilities */ + dma_dev->dev = ddma->dev; + dma_dev->chancnt = ddma->dma_channels; + dma_dev->src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); + dma_dev->dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); + dma_dev->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); + dma_dev->residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR; + + /* function callback */ + dma_dev->device_tx_status = phytium_ddma_tx_status; + dma_dev->device_issue_pending = phytium_ddma_issue_pending; + dma_dev->device_terminate_all = phytium_ddma_terminate_all; + dma_dev->device_alloc_chan_resources = + phytium_ddma_alloc_chan_resources; + dma_dev->device_free_chan_resources = phytium_ddma_free_chan_resources; + dma_dev->device_config = phytium_ddma_slave_config; + dma_dev->device_prep_slave_sg = phytium_ddma_prep_slave_sg; + dma_dev->device_pause = phytium_chan_pause; + dma_dev->device_resume = phytium_chan_resume; + + /* init dma physical channels */ + INIT_LIST_HEAD(&dma_dev->channels); + ddma->chan = devm_kcalloc(ddma->dev, ddma->dma_channels, + sizeof(*ddma->chan), GFP_KERNEL); + if (!ddma->chan) { + ret = -ENOMEM; + goto out; + } + for (i = 0; i < ddma->dma_channels; i++) { + ddma->chan[i].id = i; + ddma->chan[i].buf = NULL; + ddma->chan[i].base = ddma->base + DMA_REG_LEN + + i * CHAN_REG_LEN; + ddma->chan[i].vchan.desc_free = phytium_ddma_vdesc_free; + ddma->chan[i].desc = NULL; + ddma->chan[i].current_sg = NULL; + vchan_init(&ddma->chan[i].vchan, dma_dev); + } + + phytium_dma_hw_init(ddma); + + ret = dma_async_device_register(dma_dev); + if (ret) + goto out; + + ret = of_dma_controller_register(pdev->dev.of_node, + phytium_ddma_of_xlate, ddma); + if (ret < 0) { + dev_err(&pdev->dev, + "phytium ddma of register failed %d\n", ret); + goto err_unregister; + } + + platform_set_drvdata(pdev, ddma); + dev_info(ddma->dev, "phytium DDMA Controller registered\n"); + + return 0; + +err_unregister: + dma_async_device_unregister(dma_dev); + +out: + return ret; +} + +static void phytium_ddma_chan_remove(struct phytium_ddma_chan *chan) +{ + phytium_chan_irq_disable(chan); + phytium_chan_disable(chan); + + if (chan->buf) + dma_free_coherent(chan_to_dev(chan), 4 * PAGE_SIZE, chan->buf, + chan->paddr); + + tasklet_kill(&chan->vchan.task); + list_del(&chan->vchan.chan.device_node); +} + +static int phytium_ddma_remove(struct platform_device *pdev) +{ + struct phytium_ddma_device *ddma = platform_get_drvdata(pdev); + struct phytium_ddma_chan *chan = NULL; + int i = 0; + + of_dma_controller_free(pdev->dev.of_node); + dma_async_device_unregister(&ddma->dma_dev); + + for (i = 0; i < ddma->dma_channels; i++) { + chan = &ddma->chan[i]; + phytium_ddma_chan_remove(chan); + } + + phytium_ddma_irq_disable(ddma); + phytium_ddma_disable(ddma); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int phytium_ddma_suspend(struct device *dev) +{ + struct phytium_ddma_device *ddma = dev_get_drvdata(dev); + int i = 0; + + for (i = 0; i < ddma->dma_channels; i++) { + if (phytium_chan_is_running(&ddma->chan[i])) { + dev_warn(dev, + "suspend is prevented by channel %d\n", i); + return -EBUSY; + } + } + + ddma->dma_reg.dma_chal_cfg0 = + phytium_ddma_ioread32(ddma, DMA_CHAL_CFG_L); + ddma->dma_reg.dma_chal_bind = + phytium_ddma_ioread32(ddma, DMA_CHAL_BIND); + ddma->dma_reg.dma_chal_cfg1 = + phytium_ddma_ioread32(ddma, DMA_CHAL_CFG_H); + + for (i = 0; i < ddma->dma_channels; i++) { + struct phytium_ddma_chan *chan = &ddma->chan[i]; + + if (!chan->is_used) + continue; + ddma->dma_chal_reg[i].dma_chalx_ctl = + phytium_chan_ioread32(chan, DMA_CHALX_CTL); + ddma->dma_chal_reg[i].dma_chalx_timeout_cnt = + phytium_chan_ioread32(chan, DMA_CHALX_TIMEOUT_CNT); + } + + phytium_ddma_irq_disable(ddma); + phytium_ddma_disable(ddma); + pm_runtime_force_suspend(dev); + + return 0; +} + +static int phytium_ddma_resume(struct device *dev) +{ + struct phytium_ddma_device *ddma = dev_get_drvdata(dev); + u32 i = 0; + int ret = 0; + + phytium_dma_hw_init(ddma); + phytium_ddma_iowrite32(ddma, DMA_CHAL_CFG_L, + ddma->dma_reg.dma_chal_cfg0); + phytium_ddma_iowrite32(ddma, DMA_CHAL_BIND, + ddma->dma_reg.dma_chal_bind); + phytium_ddma_iowrite32(ddma, DMA_CHAL_CFG_H, + ddma->dma_reg.dma_chal_cfg1); + + for (i = 0; i < ddma->dma_channels; i++) { + struct phytium_ddma_chan *chan = &ddma->chan[i]; + + if (!chan->is_used) + continue; + phytium_chan_iowrite32(chan, DMA_CHALX_CTL, + ddma->dma_chal_reg[i].dma_chalx_ctl); + phytium_chan_iowrite32(chan, DMA_CHALX_TIMEOUT_CNT, + ddma->dma_chal_reg[i].dma_chalx_timeout_cnt); + } + + ret = pm_runtime_force_resume(dev); + + return ret; +} +#endif + +static const struct dev_pm_ops phytium_ddma_pm_ops = { + SET_LATE_SYSTEM_SLEEP_PM_OPS(phytium_ddma_suspend, + phytium_ddma_resume) +}; + +static const struct of_device_id phytium_dma_of_id_table[] = { + { .compatible = "phytium,ddma" }, + {} +}; +MODULE_DEVICE_TABLE(of, phytium_dma_of_id_table); + +static struct platform_driver phytium_driver = { + .probe = phytium_ddma_probe, + .remove = phytium_ddma_remove, + .driver = { + .name = "phytium-ddma", + .of_match_table = of_match_ptr(phytium_dma_of_id_table), + .pm = &phytium_ddma_pm_ops, + }, +}; + +module_platform_driver(phytium_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Phytium DDMA Controller platform driver"); +MODULE_AUTHOR("HuangJie "); diff --git a/target/linux/phytium/files-5.10/drivers/dma/phytium/phytium-ddmac.h b/target/linux/phytium/files-5.10/drivers/dma/phytium/phytium-ddmac.h new file mode 100644 index 000000000..81bc0e19e --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/dma/phytium/phytium-ddmac.h @@ -0,0 +1,164 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Phytium Device DDMA Controller driver. + * + * Copyright (c) 2023 Phytium Technology Co., Ltd. + */ + +#ifndef _PHYTIUM_DDMAC_H +#define _PHYTIUM_DDMAC_H + +#include +#include +#include +#include +#include +#include "../virt-dma.h" + +/* the number of physical channel */ +#define DDMA_MAX_NR_PCHANNELS 8 + +#define DMAC_MAX_MASTERS 1 +#define DMAC_MAX_BLK_SIZE PAGE_SIZE + +#define CHAN_REG_LEN 0x40 +#define DMA_REG_LEN 0x40 + +#define DMA_CTL 0x00 +#define DMA_CHAL_CFG_L 0x04 +#define DMA_CHAL_CFG_H 0x28 +#define DMA_STAT 0x08 +#define DMA_MASK_INT 0x0C +#define DMA_CHAL_BIND 0x20 +#define DMA_GCAP 0x24 + +#define DMA_CHALX_DDR_UPADDR 0x00 +#define DMA_CHALX_DDR_LWADDR 0x04 +#define DMA_CHALX_DEV_ADDR 0x08 +#define DMA_CHALX_TS 0x0C +#define DMA_CHALX_CRT_UPADDR 0x10 +#define DMA_CHALX_CRT_LWADDR 0x14 +#define DMA_CHALX_CTL 0x18 +#define DMA_CHALX_STS 0x1C +#define DMA_CHALX_TIMEOUT_CNT 0x20 +#define DMA_CHALX_TRANS_CNT 0x24 + +#define DMA_CTL_EN BIT(0) +#define DMA_CTL_SRST BIT(1) + +#define DMA_CHAL_SEL(id, x) (min_t(unsigned int, x, 0x7F) << ((id) * 8)) +#define DMA_CHAL_SEL_EN(id) BIT((id) * 8 + 7) + +#define DMA_STAT_CHAL(id) BIT((id) * 4) + +#define DMA_INT_EN BIT(31) +#define DMA_INT_CHAL_EN(id) BIT(id) + +#define DMA_CHAL_EN BIT(0) +#define DMA_CHAL_SRST BIT(1) +#define DMA_CHAL_MODE BIT(2) + +#define DMA_RX_REQ 1 +#define DMA_TX_REQ 0 + +#define DMA_CHAL_TIMEOUT_EN BIT(31) +#define DMA_CHAL_TIMEOUT_CNT(x) min_t(unsigned int, x, 0xFFFFF) + +#define DMA_TIMEOUT 10 + +/** + * struct phytium_ddma_sg_req - scatter-gatter list data info + * @len: number of bytes to transform + * @mem_addr_l: bus address low 32bit + * @mem_addr_h: bus address high 32bit + * @dev_addr: dma cousumer data reg addr + * @direction: dma transmit direction + */ +struct phytium_ddma_sg_req { + u32 len; + u32 mem_addr_l; + u32 mem_addr_h; + u32 dev_addr; + enum dma_transfer_direction direction; +}; + +/** + * struct phytium_ddma_desc - the struct holding info describing ddma request + * descriptor + * @vdesc: ddma request descriptor + * @num_sgs: the size of scatter-gatter list + * @sg_req: use to save scatter-gatter list info + */ +struct phytium_ddma_desc { + struct virt_dma_desc vdesc; + u32 num_sgs; + struct phytium_ddma_sg_req sg_req[]; +}; + +/** + * struct phytium_ddma_chan - the struct holding info describing dma channel + * @vchan: virtual dma channel + * @base: the mapped register I/O of dma physical channel + * @id: the id of ddma physical channel + * @request_line: the request line of ddma channel + * @desc: the transform request descriptor + * @dma_config: config parameters for dma channel + * @busy: the channel busy flag, this flag set when channel is tansferring + * @is_used: the channel bind flag, this flag set when channel binded + * @next_sg: the index of next scatter-gatter + * @current_sg: use to save the current transfer scatter-gatter info + * @paddr: use to align data between dma provider and consumer + */ +struct phytium_ddma_chan { + struct virt_dma_chan vchan; + void __iomem *base; + u32 id; + u32 request_line; + struct phytium_ddma_desc *desc; + struct dma_slave_config dma_config; + bool busy; + bool is_used; + bool is_pasued; + u32 next_sg; + struct phytium_ddma_sg_req *current_sg; + dma_addr_t paddr; + char *buf; +}; + +struct global_reg { + u32 dma_chal_cfg0; + u32 dma_chal_bind; + u32 dma_chal_cfg1; +}; + +struct channel_reg { + u32 dma_chalx_ctl; + u32 dma_chalx_timeout_cnt; +}; + +/** + * struct phytium_ddma_device - the struct holding info describing DDMA device + * @dma_dev: an instance for struct dma_device + * @irq: the irq that DDMA using + * @base: the mapped register I/O base of this DDMA + * @core_clk: DDMA clock + * @dma_channels: the number of DDMA physical channels + * @chan: the phyical channels of DDMA + * @lock: spinlock to lock when set global registers + * @dma_reg: store global register value which need recover after resume + * @dma_chal_reg: store channel register value which need recover after resume + */ +struct phytium_ddma_device { + struct dma_device dma_dev; + struct device *dev; + int irq; + void __iomem *base; + struct clk *core_clk; + u32 dma_channels; + struct phytium_ddma_chan *chan; + spinlock_t lock; + struct global_reg dma_reg; + struct channel_reg dma_chal_reg[DDMA_MAX_NR_PCHANNELS]; +}; + +#endif /* _PHYTIUM_DDMAC_H */ diff --git a/target/linux/phytium/files-5.10/drivers/edac/phytium_edac.c b/target/linux/phytium/files-5.10/drivers/edac/phytium_edac.c new file mode 100644 index 000000000..d16ac658d --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/edac/phytium_edac.c @@ -0,0 +1,485 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Phytium Pe220x EDAC (error detection and correction) + * + * Copyright (c) 2023 Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "edac_module.h" + +#define EDAC_MOD_STR "phytium_edac" + +/* register offset */ +#define ERR_STATUS(n) (0x10 + ((n) * 64)) +#define ERR_CTLR(n) (0x08 + ((n) * 64)) +#define ERR_MISC0(n) (0x20 + ((n) * 64)) +#define ERR_INJECT 0x7C +#define ERR_DEVID 0xFC8 +#define ERR_GSR 0xE00 + +#define CTLR_ED BIT(0) +#define CTLR_UI BIT(2) +#define CTLR_CFI BIT(8) + +#define MISC0_CEC(x) ((u64)(x) << 32) + +#define ERR_STATUS_CLEAR GENMASK(31, 0) + +#define CORRECTED_ERROR 0 +#define UNCORRECTED_ERROR 1 + +#define MAX_ERR_GROUP 3 + +struct phytium_edac { + struct device *dev; + void __iomem **ras_base; + struct dentry *dfs; + struct edac_device_ctl_info *edac_dev; +}; + +struct ras_error_info { + u32 index; + u32 error_type; + const char *error_str; +}; + +/* error severity definition */ +enum { + SEV_NO = 0x0, + SEV_CORRECTED = 0x1, + SEV_RECOVERABLE = 0x2, + SEV_PANIC = 0x3, +}; + +/* soc error record */ +static const struct ras_error_info pe220x_ras_soc_error[] = { + { 0, UNCORRECTED_ERROR, "lsd_nfc_ras_error" }, + { 1, UNCORRECTED_ERROR, "lsd_lpc_ras_long_wait_to" }, + { 2, UNCORRECTED_ERROR, "lsd_lpc_ras_short_wait_to" }, + { 3, UNCORRECTED_ERROR, "lsd_lpc_ras_sync_err" }, + { 4, UNCORRECTED_ERROR, "lsd_lbc_ras_err" }, + { 5, UNCORRECTED_ERROR, "usb3_err_0" }, + { 6, UNCORRECTED_ERROR, "usb3_err_1" }, + { 7, UNCORRECTED_ERROR, "gsd_gmu_mac0_asf_nonfatal_int" }, + { 8, UNCORRECTED_ERROR, "gsd_gmu_mac0_asf_fatal_int" }, + { 9, UNCORRECTED_ERROR, "gsd_gmu_mac0_asf_trans_to_err" }, + { 10, UNCORRECTED_ERROR, "gsd_gmu_mac0_asf_protocol_err" }, + { 11, UNCORRECTED_ERROR, "gsd_gmu_mac1_asf_nonfatal_int" }, + { 12, UNCORRECTED_ERROR, "gsd_gmu_mac1_asf_fatal_int" }, + { 13, UNCORRECTED_ERROR, "gsd_gmu_mac1_asf_trans_to_err" }, + { 14, UNCORRECTED_ERROR, "gsd_gmu_mac1_asf_protocol_err" }, + { 15, UNCORRECTED_ERROR, "gsd_gmu_mac2_asf_nonfatal_int" }, + { 16, UNCORRECTED_ERROR, "gsd_gmu_mac2_asf_fatal_int" }, + { 17, UNCORRECTED_ERROR, "gsd_gmu_mac2_asf_trans_to_err" }, + { 18, UNCORRECTED_ERROR, "gsd_gmu_mac2_asf_protocol_err" }, + { 19, UNCORRECTED_ERROR, "gsd_gmu_mac3_asf_nonfatal_int" }, + { 20, UNCORRECTED_ERROR, "gsd_gmu_mac3_asf_fatal_int" }, + { 21, UNCORRECTED_ERROR, "gsd_gmu_mac3_asf_trans_to_err" }, + { 22, UNCORRECTED_ERROR, "gsd_gmu_mac3_asf_protocol_err" }, + { 23, CORRECTED_ERROR, "dmu_ras_ecc_corrected_error" }, + { 24, UNCORRECTED_ERROR, "dmu_ras_ecc_uncorrected_error" }, + { 25, UNCORRECTED_ERROR, "cci_ras_nERRIRQ" }, + { 26, UNCORRECTED_ERROR, "smmu_tcu_ras_irpt" }, + { 27, UNCORRECTED_ERROR, "smmu_tbu0_ras_irpt" }, + { 28, UNCORRECTED_ERROR, "smmu_tbu1_ras_irpt" }, + { 29, UNCORRECTED_ERROR, "smmu_tbu2_ras_irpt" }, + { 30, UNCORRECTED_ERROR, "ocm_sram_ue" }, + { 31, CORRECTED_ERROR, "ocm_sram_ce" }, + { 32, UNCORRECTED_ERROR, "int_axim_err" }, + { 33, UNCORRECTED_ERROR, "int_fatal_error" }, + { 34, UNCORRECTED_ERROR, "nEXTERRIRQ_clust0" }, + { 35, UNCORRECTED_ERROR, "nINTERRIRQ_clust0" }, + { 36, UNCORRECTED_ERROR, "nEXTERRIRQ_clust1" }, + { 37, UNCORRECTED_ERROR, "nINTERRIRQ_clust1" }, + { 38, UNCORRECTED_ERROR, "nEXTERRIRQ_clust2" }, + { 39, UNCORRECTED_ERROR, "nINTERRIRQ_clust2" }, + { 40, UNCORRECTED_ERROR, "ams_ame0_ras_err" }, + { 41, UNCORRECTED_ERROR, "ams_ame1_ras_err" }, + { 42, UNCORRECTED_ERROR, "ams_amer_ras_err" }, + { 43, UNCORRECTED_ERROR, "ras_err_ame1" }, +}; + +/* pcie controller error record */ +static const struct ras_error_info pe220x_ras_peu_psu_error[] = { + { 0, CORRECTED_ERROR, "pio_rd_addr_error" }, + { 1, UNCORRECTED_ERROR, "pio_wr_addr_error" }, + { 2, CORRECTED_ERROR, "pio_rd_timeout" }, + { 3, CORRECTED_ERROR, "pio_wr_timeout" }, + { 4, CORRECTED_ERROR, "axi_b_rsp_error" }, + { 5, CORRECTED_ERROR, "axi_r_rsp_error" }, +}; + +static const struct ras_error_info pe220x_ras_peu_error[] = { + { 0, CORRECTED_ERROR, "pio_rd_addr_error" }, + { 1, UNCORRECTED_ERROR, "pio_wr_addr_error" }, + { 2, CORRECTED_ERROR, "pio_rd_timeout" }, + { 3, CORRECTED_ERROR, "pio_wr_timeout" }, + { 4, CORRECTED_ERROR, "axi_b_rsp_error" }, + { 5, CORRECTED_ERROR, "axi_r_rsp_error" }, +}; + +static const struct ras_error_info *pe220x_ras_error[] = { + pe220x_ras_soc_error, pe220x_ras_peu_psu_error, pe220x_ras_peu_error +}; + +static inline unsigned int get_error_num(const struct phytium_edac *edac, + int err_group) +{ + unsigned int error_num = 0; + + error_num = readl(edac->ras_base[err_group] + ERR_DEVID); + + return error_num; +} + +static inline void phytium_ras_setup(const struct phytium_edac *edac) +{ + u64 val = 0; + unsigned int i = 0; + /* + * enable error report and generate interrupt for corrected error event + * first error record owned by node present the node configuration + */ + for (i = 0; i < MAX_ERR_GROUP; i++) { + val = readq(edac->ras_base[i] + ERR_CTLR(0)); + val |= CTLR_ED | CTLR_UI | CTLR_CFI; + writeq(val, edac->ras_base[i] + ERR_CTLR(0)); + } +} + +static ssize_t phytium_edac_inject_ctrl_write(struct file *filp, + const char __user *buf, + size_t size, loff_t *ppos) +{ + int ret = 0; + int res = 0; + unsigned int error_group = 0; + unsigned int error_id = 0; + unsigned int error_num = 0; + struct phytium_edac *edac = filp->private_data; + char str[255]; + char *p_str = str; + char *tmp = NULL; + + if (size > 255) { + ret = -EFAULT; + goto out; + } + + if (copy_from_user(str, buf, size)) { + ret = -EFAULT; + goto out; + } else { + *ppos += size; + ret = size; + } + str[size] = '\0'; + + tmp = strsep(&p_str, ","); + if (!tmp) + goto out; + + res = kstrtouint(tmp, 0, &error_group); + if (res || error_group >= MAX_ERR_GROUP) { + dev_err(edac->dev, "invalid error group parameters"); + goto out; + } + + res = kstrtouint(p_str, 0, &error_id); + if (res) { + dev_err(edac->dev, "invalid error id parameters"); + goto out; + } + + error_num = get_error_num(edac, error_group); + if (error_id >= error_num) { + dev_err(edac->dev, "invalid ras error id.\n"); + goto out; + } + + dev_dbg(edac->dev, "inject group%d, error_id: %d\n", + error_group, error_id); + + if (pe220x_ras_error[error_group][error_id].error_type == + CORRECTED_ERROR) { + writeq(MISC0_CEC(0xFF), + edac->ras_base[error_group] + ERR_MISC0(error_id)); + } + + writel(error_id, edac->ras_base[error_group] + ERR_INJECT); + +out: + return ret; +} + +static const struct file_operations phytium_edac_debug_inject_fops[] = { + { + .open = simple_open, + .write = phytium_edac_inject_ctrl_write, + .llseek = generic_file_llseek, }, + { } +}; + +static void phytium_edac_create_debugfs_nodes(struct phytium_edac *edac) +{ + if (!IS_ENABLED(CONFIG_EDAC_DEBUG) || !edac->dfs) { + dev_info(edac->dev, "edac debug is disable"); + return; + } + + edac_debugfs_create_file("error_inject_ctrl", S_IWUSR, edac->dfs, edac, + &phytium_edac_debug_inject_fops[0]); +} + +static int phytium_edac_device_add(struct phytium_edac *edac) +{ + struct edac_device_ctl_info *edac_dev; + int res = 0; + + edac_dev = edac_device_alloc_ctl_info( + sizeof(struct edac_device_ctl_info), + "ras", 1, "soc", 1, 0, NULL, + 0, edac_device_alloc_index()); + if (!edac_dev) + res = -ENOMEM; + + edac_dev->dev = edac->dev; + edac_dev->mod_name = EDAC_MOD_STR; + edac_dev->ctl_name = "phytium ras"; + edac_dev->dev_name = "soc"; + + phytium_edac_create_debugfs_nodes(edac); + + res = edac_device_add_device(edac_dev); + if (res > 0) { + dev_err(edac->dev, "edac_device_add_device failed\n"); + goto err_free; + } + + edac->edac_dev = edac_dev; + dev_info(edac->dev, "phytium edac device registered\n"); + return 0; + +err_free: + edac_device_free_ctl_info(edac_dev); + return res; +} + +static int phytium_edac_device_remove(struct phytium_edac *edac) +{ + struct edac_device_ctl_info *edac_dev = edac->edac_dev; + + debugfs_remove_recursive(edac->dfs); + edac_device_del_device(edac_dev->dev); + edac_device_free_ctl_info(edac_dev); + return 0; +} + +static int get_error_id(struct phytium_edac *edac, int *error_id, + int *error_group) +{ + unsigned int error_num = 0; + u64 error_bit = 0; + int ret = 0; + int i = 0; + int err_id = 0; + + /* Iterate over the ras node to check error status */ + for (i = 0; i < MAX_ERR_GROUP; i++) { + error_num = get_error_num(edac, i); + error_bit = readq(edac->ras_base[i] + ERR_GSR); + for (err_id = 0; err_id < error_num; err_id++) { + if (!(error_bit & BIT(err_id))) + continue; + else + break; + } + if (err_id < error_num) { + *error_id = err_id; + *error_group = i; + break; + } + } + + if (i >= MAX_ERR_GROUP) { + ret = -1; + dev_warn(edac->dev, "no error detect.\n"); + } + + return ret; +} + +static void phytium_edac_error_report(struct phytium_edac *edac, + const int error_id, + const int error_group) +{ + const struct ras_error_info *err_info = + pe220x_ras_error[error_group]; + + if (err_info[error_id].error_type == UNCORRECTED_ERROR) { + edac_printk(KERN_CRIT, EDAC_MOD_STR, "uncorrected error: %s\n", + err_info[error_id].error_str); + edac_device_handle_ue(edac->edac_dev, 0, 0, + err_info[error_id].error_str); + /* Report the error via the trace interface */ + if (IS_ENABLED(CONFIG_RAS)) + trace_non_standard_event(&NULL_UUID_LE, &NULL_UUID_LE, + EDAC_MOD_STR, SEV_RECOVERABLE, + err_info[error_id].error_str, + strlen(err_info[error_id].error_str)); + } else { + edac_printk(KERN_CRIT, EDAC_MOD_STR, "corrected error: %s\n", + err_info[error_id].error_str); + edac_device_handle_ce(edac->edac_dev, 0, 0, + err_info[error_id].error_str); + /* Report the error via the trace interface */ + if (IS_ENABLED(CONFIG_RAS)) + trace_non_standard_event(&NULL_UUID_LE, &NULL_UUID_LE, + EDAC_MOD_STR, SEV_CORRECTED, + err_info[error_id].error_str, + strlen(err_info[error_id].error_str)); + } +} + +/* + * clear error status and set correct error counter to 0xFE for trigger + * interrupt when next correct error event + */ +static void phytium_edac_clear_error_status(struct phytium_edac *edac, + const int error_id, + const int error_group) +{ + writeq(MISC0_CEC(0XFE), edac->ras_base[error_group] + + ERR_MISC0(error_id)); + writeq(GENMASK(31, 0), edac->ras_base[error_group] + + ERR_STATUS(error_id)); +} + +static irqreturn_t phytium_edac_isr(int irq, void *dev_id) +{ + struct phytium_edac *edac = dev_id; + int ret = 0; + int error_group; + int error_id; + + ret = get_error_id(edac, &error_id, &error_group); + if (ret < 0) + goto out; + + phytium_edac_error_report(edac, error_id, error_group); + phytium_edac_clear_error_status(edac, error_id, error_group); + +out: + return IRQ_HANDLED; +} + +static int phytium_edac_probe(struct platform_device *pdev) +{ + struct phytium_edac *edac; + struct resource *res; + int ret = 0; + int irq_cnt = 0; + int irq = 0; + int i = 0; + + edac = devm_kzalloc(&pdev->dev, sizeof(*edac), GFP_KERNEL); + if (!edac) { + ret = -ENOMEM; + goto out; + } + + edac->dev = &pdev->dev; + platform_set_drvdata(pdev, edac); + + edac->ras_base = devm_kcalloc(&pdev->dev, 3, + sizeof(*edac->ras_base), GFP_KERNEL); + if (!edac->ras_base) { + return -ENOMEM; + goto out; + } + + for (i = 0; i < MAX_ERR_GROUP; i++) { + res = platform_get_resource(pdev, IORESOURCE_MEM, i); + edac->ras_base[i] = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(edac->ras_base[i])) { + dev_err(&pdev->dev, "no resource address\n"); + ret = PTR_ERR(edac->ras_base[i]); + goto out; + } + } + + edac->dfs = edac_debugfs_create_dir(EDAC_MOD_STR); + + ret = phytium_edac_device_add(edac); + if (ret) { + dev_err(&pdev->dev, "can't add edac device"); + goto out; + } + + phytium_ras_setup(edac); + + irq_cnt = platform_irq_count(pdev); + if (irq_cnt < 0) { + dev_err(&pdev->dev, "no irq resource\n"); + ret = -EINVAL; + goto out; + } + + for (i = 0; i < irq_cnt; i++) { + irq = platform_get_irq(pdev, i); + if (irq < 0) { + dev_err(&pdev->dev, "invalid irq resource\n"); + ret = -EINVAL; + goto out; + } + ret = devm_request_irq(&pdev->dev, irq, + phytium_edac_isr, IRQF_SHARED, + EDAC_MOD_STR, edac); + if (ret) { + dev_err(&pdev->dev, + "could not request irq %d\n", irq); + goto out; + } + } + +out: + return ret; +} + +static int phytium_edac_remove(struct platform_device *pdev) +{ + struct phytium_edac *edac = dev_get_drvdata(&pdev->dev); + + phytium_edac_device_remove(edac); + + return 0; +} + +static const struct of_device_id phytium_edac_of_match[] = { + { .compatible = "phytium,pe220x-edac" }, + {}, +}; +MODULE_DEVICE_TABLE(of, phytium_edac_of_match); + +static struct platform_driver phytium_edac_driver = { + .probe = phytium_edac_probe, + .remove = phytium_edac_remove, + .driver = { + .name = "phytium-edac", + .of_match_table = phytium_edac_of_match, + }, +}; + +module_platform_driver(phytium_edac_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Huangjie "); +MODULE_DESCRIPTION("Phytium Pe220x EDAC driver"); diff --git a/target/linux/phytium/files-5.10/drivers/gpio/gpio-phytium-core.c b/target/linux/phytium/files-5.10/drivers/gpio/gpio-phytium-core.c new file mode 100644 index 000000000..648e564df --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/gpio/gpio-phytium-core.c @@ -0,0 +1,363 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019-2023 Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include + +#include "gpio-phytium-core.h" + +static int get_pin_location(struct phytium_gpio *gpio, unsigned int offset, + struct pin_loc *pl) +{ + int ret; + + if (offset < gpio->ngpio[0]) { + pl->port = 0; + pl->offset = offset; + ret = 0; + } else if (offset < (gpio->ngpio[0] + gpio->ngpio[1])) { + pl->port = 1; + pl->offset = offset - gpio->ngpio[0]; + ret = 0; + } else { + ret = -EINVAL; + } + + return ret; +} + +static void phytium_gpio_toggle_trigger(struct phytium_gpio *gpio, + unsigned int offset) +{ + struct gpio_chip *gc; + u32 pol; + int val; + + /* Only port A can provide interrupt source */ + if (offset >= gpio->ngpio[0]) + return; + + gc = &gpio->gc; + + pol = readl(gpio->regs + GPIO_INT_POLARITY); + /* Just read the current value right out of the data register */ + val = gc->get(gc, offset); + if (val) + pol &= ~BIT(offset); + else + pol |= BIT(offset); + + writel(pol, gpio->regs + GPIO_INT_POLARITY); +} + +int phytium_gpio_get(struct gpio_chip *gc, unsigned int offset) +{ + struct phytium_gpio *gpio = gpiochip_get_data(gc); + struct pin_loc loc; + void __iomem *dat; + + if (get_pin_location(gpio, offset, &loc)) + return -EINVAL; + + dat = gpio->regs + GPIO_EXT_PORTA + (loc.port * GPIO_PORT_STRIDE); + + return !!(readl(dat) & BIT(loc.offset)); +} +EXPORT_SYMBOL_GPL(phytium_gpio_get); + +void phytium_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) +{ + struct phytium_gpio *gpio = gpiochip_get_data(gc); + struct pin_loc loc; + void __iomem *dr; + unsigned long flags; + u32 mask; + + if (get_pin_location(gpio, offset, &loc)) + return; + dr = gpio->regs + GPIO_SWPORTA_DR + (loc.port * GPIO_PORT_STRIDE); + + raw_spin_lock_irqsave(&gpio->lock, flags); + + if (value) + mask = readl(dr) | BIT(loc.offset); + else + mask = readl(dr) & ~BIT(loc.offset); + + writel(mask, dr); + + raw_spin_unlock_irqrestore(&gpio->lock, flags); +} +EXPORT_SYMBOL_GPL(phytium_gpio_set); + +int phytium_gpio_direction_input(struct gpio_chip *gc, unsigned int offset) +{ + struct phytium_gpio *gpio = gpiochip_get_data(gc); + struct pin_loc loc; + unsigned long flags; + void __iomem *ddr; + + if (get_pin_location(gpio, offset, &loc)) + return -EINVAL; + ddr = gpio->regs + GPIO_SWPORTA_DDR + (loc.port * GPIO_PORT_STRIDE); + + raw_spin_lock_irqsave(&gpio->lock, flags); + + writel(readl(ddr) & ~(BIT(loc.offset)), ddr); + + raw_spin_unlock_irqrestore(&gpio->lock, flags); + + return 0; +} +EXPORT_SYMBOL_GPL(phytium_gpio_direction_input); + +int phytium_gpio_direction_output(struct gpio_chip *gc, unsigned int offset, + int value) +{ + struct phytium_gpio *gpio = gpiochip_get_data(gc); + struct pin_loc loc; + unsigned long flags; + void __iomem *ddr; + + if (get_pin_location(gpio, offset, &loc)) + return -EINVAL; + ddr = gpio->regs + GPIO_SWPORTA_DDR + (loc.port * GPIO_PORT_STRIDE); + + raw_spin_lock_irqsave(&gpio->lock, flags); + + writel(readl(ddr) | BIT(loc.offset), ddr); + + raw_spin_unlock_irqrestore(&gpio->lock, flags); + + phytium_gpio_set(gc, offset, value); + + return 0; +} +EXPORT_SYMBOL_GPL(phytium_gpio_direction_output); + +void phytium_gpio_irq_ack(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct phytium_gpio *gpio = gpiochip_get_data(gc); + u32 val = BIT(irqd_to_hwirq(d)); + + raw_spin_lock(&gpio->lock); + + writel(val, gpio->regs + GPIO_PORTA_EOI); + + raw_spin_unlock(&gpio->lock); +} +EXPORT_SYMBOL_GPL(phytium_gpio_irq_ack); + +void phytium_gpio_irq_mask(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct phytium_gpio *gpio = gpiochip_get_data(gc); + u32 val; + + /* Only port A can provide interrupt source */ + if (irqd_to_hwirq(d) >= gpio->ngpio[0]) + return; + + raw_spin_lock(&gpio->lock); + + val = readl(gpio->regs + GPIO_INTMASK); + val |= BIT(irqd_to_hwirq(d)); + writel(val, gpio->regs + GPIO_INTMASK); + + raw_spin_unlock(&gpio->lock); +} +EXPORT_SYMBOL_GPL(phytium_gpio_irq_mask); + +void phytium_gpio_irq_unmask(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct phytium_gpio *gpio = gpiochip_get_data(gc); + u32 val; + + /* Only port A can provide interrupt source */ + if (irqd_to_hwirq(d) >= gpio->ngpio[0]) + return; + + raw_spin_lock(&gpio->lock); + + val = readl(gpio->regs + GPIO_INTMASK); + val &= ~BIT(irqd_to_hwirq(d)); + writel(val, gpio->regs + GPIO_INTMASK); + + raw_spin_unlock(&gpio->lock); +} +EXPORT_SYMBOL_GPL(phytium_gpio_irq_unmask); + +int phytium_gpio_irq_set_type(struct irq_data *d, unsigned int flow_type) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct phytium_gpio *gpio = gpiochip_get_data(gc); + int hwirq = irqd_to_hwirq(d); + unsigned long flags, lvl, pol; + + if (hwirq < 0 || hwirq >= gpio->ngpio[0]) + return -EINVAL; + + if ((flow_type & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) && + (flow_type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING))) { + dev_err(gc->parent, + "trying to configure line %d for both level and edge detection, choose one!\n", + hwirq); + return -EINVAL; + } + + raw_spin_lock_irqsave(&gpio->lock, flags); + + lvl = readl(gpio->regs + GPIO_INTTYPE_LEVEL); + pol = readl(gpio->regs + GPIO_INT_POLARITY); + + switch (flow_type) { + case IRQ_TYPE_EDGE_BOTH: + lvl |= BIT(hwirq); + phytium_gpio_toggle_trigger(gpio, hwirq); + irq_set_handler_locked(d, handle_edge_irq); + dev_dbg(gc->parent, "line %d: IRQ on both edges\n", hwirq); + break; + case IRQ_TYPE_EDGE_RISING: + lvl |= BIT(hwirq); + pol |= BIT(hwirq); + irq_set_handler_locked(d, handle_edge_irq); + dev_dbg(gc->parent, "line %d: IRQ on RISING edge\n", hwirq); + break; + case IRQ_TYPE_EDGE_FALLING: + lvl |= BIT(hwirq); + pol &= ~BIT(hwirq); + irq_set_handler_locked(d, handle_edge_irq); + dev_dbg(gc->parent, "line %d: IRQ on FALLING edge\n", hwirq); + break; + case IRQ_TYPE_LEVEL_HIGH: + lvl &= ~BIT(hwirq); + pol |= BIT(hwirq); + irq_set_handler_locked(d, handle_level_irq); + dev_dbg(gc->parent, "line %d: IRQ on HIGH level\n", hwirq); + break; + case IRQ_TYPE_LEVEL_LOW: + lvl &= ~BIT(hwirq); + pol &= ~BIT(hwirq); + irq_set_handler_locked(d, handle_level_irq); + dev_dbg(gc->parent, "line %d: IRQ on LOW level\n", hwirq); + break; + } + + writel(lvl, gpio->regs + GPIO_INTTYPE_LEVEL); + if (flow_type != IRQ_TYPE_EDGE_BOTH) + writel(pol, gpio->regs + GPIO_INT_POLARITY); + + raw_spin_unlock_irqrestore(&gpio->lock, flags); + + return 0; +} +EXPORT_SYMBOL_GPL(phytium_gpio_irq_set_type); + +void phytium_gpio_irq_enable(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct phytium_gpio *gpio = gpiochip_get_data(gc); + unsigned long flags; + u32 val; + + /* Only port A can provide interrupt source */ + if (irqd_to_hwirq(d) >= gpio->ngpio[0]) + return; + + raw_spin_lock_irqsave(&gpio->lock, flags); + + val = readl(gpio->regs + GPIO_INTEN); + val |= BIT(irqd_to_hwirq(d)); + writel(val, gpio->regs + GPIO_INTEN); + + raw_spin_unlock_irqrestore(&gpio->lock, flags); +} +EXPORT_SYMBOL_GPL(phytium_gpio_irq_enable); + +void phytium_gpio_irq_disable(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct phytium_gpio *gpio = gpiochip_get_data(gc); + unsigned long flags; + u32 val; + + /* Only port A can provide interrupt source */ + if (irqd_to_hwirq(d) >= gpio->ngpio[0]) + return; + + raw_spin_lock_irqsave(&gpio->lock, flags); + + val = readl(gpio->regs + GPIO_INTEN); + val &= ~BIT(irqd_to_hwirq(d)); + writel(val, gpio->regs + GPIO_INTEN); + + raw_spin_unlock_irqrestore(&gpio->lock, flags); +} +EXPORT_SYMBOL_GPL(phytium_gpio_irq_disable); + +void phytium_gpio_irq_handler(struct irq_desc *desc) +{ + struct gpio_chip *gc = irq_desc_get_handler_data(desc); + struct phytium_gpio *gpio = gpiochip_get_data(gc); + struct irq_chip *irqchip = irq_desc_get_chip(desc); + unsigned long pending; + int offset; + + chained_irq_enter(irqchip, desc); + + pending = readl(gpio->regs + GPIO_INTSTATUS); + if (pending) { + for_each_set_bit(offset, &pending, gpio->ngpio[0]) { + int gpio_irq = irq_find_mapping(gc->irq.domain, + offset); + generic_handle_irq(gpio_irq); + + if ((irq_get_trigger_type(gpio_irq) & + IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_BOTH) + phytium_gpio_toggle_trigger(gpio, offset); + } + } + + chained_irq_exit(irqchip, desc); +} +EXPORT_SYMBOL_GPL(phytium_gpio_irq_handler); + +int phytium_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) +{ + struct phytium_gpio *gpio = gpiochip_get_data(gc); + struct pin_loc loc; + void __iomem *ddr; + + if (get_pin_location(gpio, offset, &loc)) + return -EINVAL; + ddr = gpio->regs + GPIO_SWPORTA_DDR + (loc.port * GPIO_PORT_STRIDE); + + return !(readl(ddr) & BIT(loc.offset)); +} +EXPORT_SYMBOL_GPL(phytium_gpio_get_direction); + +#if CONFIG_SMP +int +phytium_gpio_irq_set_affinity(struct irq_data *d, const struct cpumask *mask_val, bool force) +{ + struct gpio_chip *chip_data = irq_data_get_irq_chip_data(d); + struct irq_chip *chip = irq_get_chip(*(chip_data->irq.parents)); + struct irq_data *data = irq_get_irq_data(*(chip_data->irq.parents)); + + if (chip && chip->irq_set_affinity) + return chip->irq_set_affinity(data, mask_val, force); + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(phytium_gpio_irq_set_affinity); +#endif + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Phytium GPIO Controller core"); diff --git a/target/linux/phytium/files-5.10/drivers/gpio/gpio-phytium-core.h b/target/linux/phytium/files-5.10/drivers/gpio/gpio-phytium-core.h new file mode 100644 index 000000000..cafca7807 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/gpio/gpio-phytium-core.h @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#ifndef _GPIO_PHYTIUM_H +#define _GPIO_PHYTIUM_H + +#include +#include + +#include "gpiolib.h" + +#define GPIO_SWPORTA_DR 0x00 /* WR Port A Output Data Register */ +#define GPIO_SWPORTA_DDR 0x04 /* WR Port A Data Direction Register */ +#define GPIO_EXT_PORTA 0x08 /* RO Port A Input Data Register */ +#define GPIO_SWPORTB_DR 0x0c /* WR Port B Output Data Register */ +#define GPIO_SWPORTB_DDR 0x10 /* WR Port B Data Direction Register */ +#define GPIO_EXT_PORTB 0x14 /* RO Port B Input Data Register */ + +#define GPIO_INTEN 0x18 /* WR Port A Interrput Enable Register */ +#define GPIO_INTMASK 0x1c /* WR Port A Interrupt Mask Register */ +#define GPIO_INTTYPE_LEVEL 0x20 /* WR Port A Interrupt Level Register */ +#define GPIO_INT_POLARITY 0x24 /* WR Port A Interrupt Polarity Register */ +#define GPIO_INTSTATUS 0x28 /* RO Port A Interrupt Status Register */ +#define GPIO_RAW_INTSTATUS 0x2c /* RO Port A Raw Interrupt Status Register */ +#define GPIO_LS_SYNC 0x30 /* WR Level-sensitive Synchronization Enable Register */ +#define GPIO_DEBOUNCE 0x34 /* WR Debounce Enable Register */ +#define GPIO_PORTA_EOI 0x38 /* WO Port A Clear Interrupt Register */ + +#define MAX_NPORTS 2 +#define NGPIO_DEFAULT 8 +#define NGPIO_MAX 32 +#define GPIO_PORT_STRIDE (GPIO_EXT_PORTB - GPIO_EXT_PORTA) + +struct pin_loc { + unsigned int port; + unsigned int offset; +}; + +#ifdef CONFIG_PM_SLEEP +struct phytium_gpio_ctx { + u32 swporta_dr; + u32 swporta_ddr; + u32 ext_porta; + u32 swportb_dr; + u32 swportb_ddr; + u32 ext_portb; + u32 inten; + u32 intmask; + u32 inttype_level; + u32 int_polarity; + u32 intstatus; + u32 raw_intstatus; + u32 ls_sync; + u32 debounce; +}; +#endif + +struct phytium_gpio { + raw_spinlock_t lock; + void __iomem *regs; + struct gpio_chip gc; + struct irq_chip irq_chip; + unsigned int ngpio[2]; + int irq[32]; +#ifdef CONFIG_PM_SLEEP + struct phytium_gpio_ctx ctx; +#endif +}; + +int phytium_gpio_get(struct gpio_chip *gc, unsigned int offset); +void phytium_gpio_set(struct gpio_chip *gc, unsigned int offset, int value); + +int phytium_gpio_get_direction(struct gpio_chip *gc, unsigned int offset); +int phytium_gpio_direction_input(struct gpio_chip *gc, unsigned int offset); +int phytium_gpio_direction_output(struct gpio_chip *gc, unsigned int offset, int value); + +void phytium_gpio_irq_ack(struct irq_data *d); +void phytium_gpio_irq_mask(struct irq_data *d); +void phytium_gpio_irq_unmask(struct irq_data *d); +int phytium_gpio_irq_set_type(struct irq_data *d, unsigned int flow_type); +void phytium_gpio_irq_enable(struct irq_data *d); +void phytium_gpio_irq_disable(struct irq_data *d); +void phytium_gpio_irq_handler(struct irq_desc *desc); +int phytium_gpio_irq_set_affinity(struct irq_data *d, const struct cpumask *mask_val, bool force); +#endif diff --git a/target/linux/phytium/files-5.10/drivers/gpio/gpio-phytium-pci.c b/target/linux/phytium/files-5.10/drivers/gpio/gpio-phytium-pci.c new file mode 100644 index 000000000..60563c59a --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/gpio/gpio-phytium-pci.c @@ -0,0 +1,187 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "gpio-phytium-core.h" + +static int phytium_gpio_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct device *dev = &pdev->dev; + struct phytium_gpio *gpio; + struct gpio_irq_chip *girq; + int err; + + gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL); + if (!gpio) + return -ENOMEM; + + err = pcim_enable_device(pdev); + if (err) { + dev_err(dev, "Failed to enable PCI device: err %d\n", err); + goto out; + } + + err = pcim_iomap_regions(pdev, 1 << 0, pci_name(pdev)); + if (err) { + dev_err(dev, "Failed to iomap PCI device: err %d\n", err); + goto out; + } + + gpio->regs = pcim_iomap_table(pdev)[0]; + if (!gpio->regs) { + dev_err(dev, "Cannot map PCI resource\n"); + err = -ENOMEM; + goto out; + } + + err = pci_enable_msi(pdev); + if (err < 0) + goto out; + + pci_set_master(pdev); + + gpio->irq[0] = pdev->irq; + if (gpio->irq[0] < 0) + dev_warn(dev, "no irq is found.\n"); + + /* There is only one group of Pins at the moment. */ + gpio->ngpio[0] = NGPIO_MAX; + + /* irq_chip support */ + gpio->irq_chip.name = dev_name(dev); + gpio->irq_chip.irq_ack = phytium_gpio_irq_ack; + gpio->irq_chip.irq_mask = phytium_gpio_irq_mask; + gpio->irq_chip.irq_unmask = phytium_gpio_irq_unmask; + gpio->irq_chip.irq_set_type = phytium_gpio_irq_set_type; + gpio->irq_chip.irq_enable = phytium_gpio_irq_enable; + gpio->irq_chip.irq_disable = phytium_gpio_irq_disable; + + raw_spin_lock_init(&gpio->lock); + + gpio->gc.base = -1; + gpio->gc.get_direction = phytium_gpio_get_direction; + gpio->gc.direction_input = phytium_gpio_direction_input; + gpio->gc.direction_output = phytium_gpio_direction_output; + gpio->gc.get = phytium_gpio_get; + gpio->gc.set = phytium_gpio_set; + gpio->gc.ngpio = gpio->ngpio[0] + gpio->ngpio[1]; + gpio->gc.label = dev_name(dev); + gpio->gc.parent = dev; + gpio->gc.owner = THIS_MODULE; + + girq = &gpio->gc.irq; + girq->handler = handle_bad_irq; + girq->default_type = IRQ_TYPE_NONE; + + girq->num_parents = 1; + girq->parents = devm_kcalloc(&pdev->dev, girq->num_parents, + sizeof(*girq->parents), GFP_KERNEL); + if (!girq->parents) + return -ENOMEM; + girq->parents[0] = gpio->irq[0]; + girq->parent_handler = phytium_gpio_irq_handler; + + girq->chip = &gpio->irq_chip; + + err = devm_gpiochip_add_data(dev, &gpio->gc, gpio); + if (err) + goto out; + + dev_info(dev, "Phytium PCI GPIO controller @%pa registered\n", + &gpio->regs); + + pci_set_drvdata(pdev, gpio); + +out: + return err; +} + +static const struct pci_device_id phytium_gpio_pci_ids[] = { + { PCI_DEVICE(0x1DB7, 0xDC31) }, + { 0 } +}; +MODULE_DEVICE_TABLE(pci, phytium_gpio_pci_ids); + +#ifdef CONFIG_PM_SLEEP +static int phytium_gpio_pci_suspend(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct phytium_gpio *gpio = pci_get_drvdata(pdev); + unsigned long flags; + + raw_spin_lock_irqsave(&gpio->lock, flags); + + gpio->ctx.swporta_dr = readl(gpio->regs + GPIO_SWPORTA_DR); + gpio->ctx.swporta_ddr = readl(gpio->regs + GPIO_SWPORTA_DDR); + gpio->ctx.ext_porta = readl(gpio->regs + GPIO_EXT_PORTA); + gpio->ctx.swportb_dr = readl(gpio->regs + GPIO_SWPORTB_DR); + gpio->ctx.swportb_ddr = readl(gpio->regs + GPIO_SWPORTB_DDR); + gpio->ctx.ext_portb = readl(gpio->regs + GPIO_EXT_PORTB); + + gpio->ctx.inten = readl(gpio->regs + GPIO_INTEN); + gpio->ctx.intmask = readl(gpio->regs + GPIO_INTMASK); + gpio->ctx.inttype_level = readl(gpio->regs + GPIO_INTTYPE_LEVEL); + gpio->ctx.int_polarity = readl(gpio->regs + GPIO_INT_POLARITY); + gpio->ctx.debounce = readl(gpio->regs + GPIO_DEBOUNCE); + + raw_spin_unlock_irqrestore(&gpio->lock, flags); + + return 0; +} + +static int phytium_gpio_pci_resume(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct phytium_gpio *gpio = pci_get_drvdata(pdev); + unsigned long flags; + + raw_spin_lock_irqsave(&gpio->lock, flags); + + writel(gpio->ctx.swporta_dr, gpio->regs + GPIO_SWPORTA_DR); + writel(gpio->ctx.swporta_ddr, gpio->regs + GPIO_SWPORTA_DDR); + writel(gpio->ctx.ext_porta, gpio->regs + GPIO_EXT_PORTA); + writel(gpio->ctx.swportb_dr, gpio->regs + GPIO_SWPORTB_DR); + writel(gpio->ctx.swportb_ddr, gpio->regs + GPIO_SWPORTB_DDR); + writel(gpio->ctx.ext_portb, gpio->regs + GPIO_EXT_PORTB); + + writel(gpio->ctx.inten, gpio->regs + GPIO_INTEN); + writel(gpio->ctx.intmask, gpio->regs + GPIO_INTMASK); + writel(gpio->ctx.inttype_level, gpio->regs + GPIO_INTTYPE_LEVEL); + writel(gpio->ctx.int_polarity, gpio->regs + GPIO_INT_POLARITY); + writel(gpio->ctx.debounce, gpio->regs + GPIO_DEBOUNCE); + + writel(0xffffffff, gpio->regs + GPIO_PORTA_EOI); + + raw_spin_unlock_irqrestore(&gpio->lock, flags); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(phytium_gpio_pci_pm_ops, + phytium_gpio_pci_suspend, + phytium_gpio_pci_resume); + +static struct pci_driver phytium_gpio_pci_driver = { + .name = "gpio-phytium-pci", + .id_table = phytium_gpio_pci_ids, + .probe = phytium_gpio_pci_probe, + .driver = { + .pm = &phytium_gpio_pci_pm_ops, + }, +}; + +module_pci_driver(phytium_gpio_pci_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Cheng Quan "); +MODULE_DESCRIPTION("Phytium GPIO PCI Driver"); diff --git a/target/linux/phytium/files-5.10/drivers/gpio/gpio-phytium-platform.c b/target/linux/phytium/files-5.10/drivers/gpio/gpio-phytium-platform.c new file mode 100644 index 000000000..0d77eb885 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/gpio/gpio-phytium-platform.c @@ -0,0 +1,204 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Support functions for Phytium GPIO + * + * Copyright (c) 2019-2023 Phytium Technology Co., Ltd. + * + * Derived from drivers/gpio/gpio-pl061.c + * Copyright (C) 2008, 2009 Provigent Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gpio-phytium-core.h" + +static const struct of_device_id phytium_gpio_of_match[] = { + { .compatible = "phytium,gpio", }, + { } +}; +MODULE_DEVICE_TABLE(of, phytium_gpio_of_match); + +static const struct acpi_device_id phytium_gpio_acpi_match[] = { + { "PHYT0001", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, phytium_gpio_acpi_match); + +static int phytium_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *res; + struct phytium_gpio *gpio; + struct gpio_irq_chip *girq; + struct fwnode_handle *fwnode; + int err, irq_count; + + gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL); + if (!gpio) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + gpio->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(gpio->regs)) + return PTR_ERR(gpio->regs); + + if (!device_get_child_node_count(dev)) + return -ENODEV; + + device_for_each_child_node(dev, fwnode) { + int idx; + + if (fwnode_property_read_u32(fwnode, "reg", &idx) || + idx >= MAX_NPORTS) { + dev_err(dev, "missing/invalid port index\n"); + fwnode_handle_put(fwnode); + return -EINVAL; + } + + if (fwnode_property_read_u32(fwnode, "ngpios", &gpio->ngpio[idx]) && + fwnode_property_read_u32(fwnode, "nr-gpios", &gpio->ngpio[idx])) { + dev_info(dev, + "failed to get number of gpios for Port%c\n", + idx ? 'B' : 'A'); + gpio->ngpio[idx] = NGPIO_DEFAULT; + } + } + + /* irq_chip support */ + gpio->irq_chip.name = dev_name(dev); + gpio->irq_chip.irq_ack = phytium_gpio_irq_ack; + gpio->irq_chip.irq_mask = phytium_gpio_irq_mask; + gpio->irq_chip.irq_unmask = phytium_gpio_irq_unmask; + gpio->irq_chip.irq_set_type = phytium_gpio_irq_set_type; + gpio->irq_chip.irq_enable = phytium_gpio_irq_enable; + gpio->irq_chip.irq_disable = phytium_gpio_irq_disable; +#ifdef CONFIG_SMP + /* TODO: use irq_chip_set_affinity_parent instead? */ + gpio->irq_chip.irq_set_affinity = phytium_gpio_irq_set_affinity; +#endif + raw_spin_lock_init(&gpio->lock); + + gpio->gc.base = -1; + gpio->gc.get_direction = phytium_gpio_get_direction; + gpio->gc.direction_input = phytium_gpio_direction_input; + gpio->gc.direction_output = phytium_gpio_direction_output; + gpio->gc.get = phytium_gpio_get; + gpio->gc.set = phytium_gpio_set; + gpio->gc.ngpio = gpio->ngpio[0] + gpio->ngpio[1]; + gpio->gc.label = dev_name(dev); + gpio->gc.parent = dev; + gpio->gc.owner = THIS_MODULE; + + girq = &gpio->gc.irq; + girq->handler = handle_bad_irq; + girq->default_type = IRQ_TYPE_NONE; + + for (irq_count = 0; irq_count < platform_irq_count(pdev); irq_count++) { + gpio->irq[irq_count] = -ENXIO; + gpio->irq[irq_count] = platform_get_irq(pdev, irq_count); + if (gpio->irq[irq_count] < 0) { + //dev_warn(dev, "no irq is found.\n"); + break; + } + }; + + girq->num_parents = irq_count; + girq->parents = gpio->irq; + girq->parent_handler = phytium_gpio_irq_handler; + + girq->chip = &gpio->irq_chip; + + err = devm_gpiochip_add_data(dev, &gpio->gc, gpio); + if (err) + return err; + + platform_set_drvdata(pdev, gpio); + dev_info(dev, "Phytium GPIO controller @%pa registered\n", + &res->start); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int phytium_gpio_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct phytium_gpio *gpio = platform_get_drvdata(pdev); + unsigned long flags; + + raw_spin_lock_irqsave(&gpio->lock, flags); + + gpio->ctx.swporta_dr = readl(gpio->regs + GPIO_SWPORTA_DR); + gpio->ctx.swporta_ddr = readl(gpio->regs + GPIO_SWPORTA_DDR); + gpio->ctx.ext_porta = readl(gpio->regs + GPIO_EXT_PORTA); + gpio->ctx.swportb_dr = readl(gpio->regs + GPIO_SWPORTB_DR); + gpio->ctx.swportb_ddr = readl(gpio->regs + GPIO_SWPORTB_DDR); + gpio->ctx.ext_portb = readl(gpio->regs + GPIO_EXT_PORTB); + + gpio->ctx.inten = readl(gpio->regs + GPIO_INTEN); + gpio->ctx.intmask = readl(gpio->regs + GPIO_INTMASK); + gpio->ctx.inttype_level = readl(gpio->regs + GPIO_INTTYPE_LEVEL); + gpio->ctx.int_polarity = readl(gpio->regs + GPIO_INT_POLARITY); + gpio->ctx.debounce = readl(gpio->regs + GPIO_DEBOUNCE); + + raw_spin_unlock_irqrestore(&gpio->lock, flags); + + return 0; +} + +static int phytium_gpio_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct phytium_gpio *gpio = platform_get_drvdata(pdev); + unsigned long flags; + + raw_spin_lock_irqsave(&gpio->lock, flags); + + writel(gpio->ctx.swporta_dr, gpio->regs + GPIO_SWPORTA_DR); + writel(gpio->ctx.swporta_ddr, gpio->regs + GPIO_SWPORTA_DDR); + writel(gpio->ctx.ext_porta, gpio->regs + GPIO_EXT_PORTA); + writel(gpio->ctx.swportb_dr, gpio->regs + GPIO_SWPORTB_DR); + writel(gpio->ctx.swportb_ddr, gpio->regs + GPIO_SWPORTB_DDR); + writel(gpio->ctx.ext_portb, gpio->regs + GPIO_EXT_PORTB); + + writel(gpio->ctx.inten, gpio->regs + GPIO_INTEN); + writel(gpio->ctx.intmask, gpio->regs + GPIO_INTMASK); + writel(gpio->ctx.inttype_level, gpio->regs + GPIO_INTTYPE_LEVEL); + writel(gpio->ctx.int_polarity, gpio->regs + GPIO_INT_POLARITY); + writel(gpio->ctx.debounce, gpio->regs + GPIO_DEBOUNCE); + + writel(0xffffffff, gpio->regs + GPIO_PORTA_EOI); + + raw_spin_unlock_irqrestore(&gpio->lock, flags); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(phytium_gpio_pm_ops, phytium_gpio_suspend, + phytium_gpio_resume); + +static struct platform_driver phytium_gpio_driver = { + .driver = { + .name = "gpio-phytium-platform", + .pm = &phytium_gpio_pm_ops, + .of_match_table = of_match_ptr(phytium_gpio_of_match), + .acpi_match_table = ACPI_PTR(phytium_gpio_acpi_match), + }, + .probe = phytium_gpio_probe, +}; + +module_platform_driver(phytium_gpio_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Chen Baozi "); +MODULE_DESCRIPTION("Phytium GPIO driver"); diff --git a/target/linux/phytium/files-5.10/drivers/gpio/gpio-phytium-sgpio.c b/target/linux/phytium/files-5.10/drivers/gpio/gpio-phytium-sgpio.c new file mode 100644 index 000000000..689cb1084 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/gpio/gpio-phytium-sgpio.c @@ -0,0 +1,302 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Phytium SGPIO Driver + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SGPIO_CTL0_REG 0x00 +#define SGPIO_CTL0_REG_ENABLE BIT(0) +#define SGPIO_CTL0_REG_RX_DISABLE BIT(1) +#define SGPIO_CTL0_REG_L3_L0 GENMASK(11, 8) +#define SGPIO_CTL0_REG_CLK_DIV_NUM GENMASK(31, 12) +#define SGPIO_CTL1_REG 0x04 +#define SGPIO_CTL1_REG_READY BIT(0) +#define SGPIO_CTL1_REG_W_UPDATA BIT(1) +#define SGPIO_CTL1_REG_OP_MODE BIT(2) +#define SGPIO_CTL1_REG_OP_STATE BIT(3) +#define SGPIO_CTL1_REG_BIT_NUM GENMASK(14, 8) +#define SGPIO_CTL1_REG_INTERVAL_TIMER GENMASK(31, 16) +#define SGPIO_SOFT_RESET_REG 0x08 +#define SGPIO_SOFT_RESET_REG_MASK BIT(0) +#define SGPIO_IRQ_REG 0x0c +#define SGPIO_IRQ_REG_MASK BIT(0) +#define SGPIO_IRQ_M_REG 0x10 +#define SGPIO_IRQ_M_REG_MASK BIT(0) +#define SGPIO_WDATA0_REG 0x14 +#define SGPIO_WDATA_REG(x) (SGPIO_WDATA0_REG + ((x) == 2 ? 3 : (x))* 4) // 0x14, 0x18, 0x20 +#define SGPIO_RDATA0_REG 0x24 +#define SGPIO_RDATA_REG(x) (SGPIO_RDATA0_REG + (x) * 4) + +#define DEFAULT_L3_L0 0 + +#define GPIO_GROUP(x) ((x) >> 6) +#define GPIO_OFFSET(x) ((x) & GENMASK(5, 0)) +#define GPIO_BIT(x) BIT(GPIO_OFFSET(x) >> 1) + +struct phytium_sgpio { + struct gpio_chip gc; + void __iomem *regs; + unsigned int ngpios; + struct clk *pclk; + + struct mutex lock; + struct completion completion; +}; + +static bool phytium_sgpio_is_input(unsigned int offset) +{ + return !(offset % 2); +} + +static int sgpio_set_value(struct gpio_chip *gc, unsigned int offset, int val) +{ + struct phytium_sgpio *gpio = gpiochip_get_data(gc); + u32 reg; + int rc = 0; + + if (phytium_sgpio_is_input(offset)) + return -EINVAL; + + reinit_completion(&gpio->completion); + + /* + * Since this is an output, read the cached value from rdata, + * then update value. + */ + /* cached data from wdata? */ + reg = readl(gpio->regs + SGPIO_WDATA_REG(GPIO_GROUP(offset))); + if (val) + reg |= GPIO_BIT(offset); + else + reg &= GPIO_BIT(offset); + writel(reg, gpio->regs + SGPIO_WDATA_REG(GPIO_GROUP(offset))); + + /* Start transmission and wait for completion */ + writel(readl(gpio->regs + SGPIO_CTL1_REG) | SGPIO_CTL1_REG_W_UPDATA, + gpio->regs + SGPIO_CTL1_REG); + if (!wait_for_completion_timeout(&gpio->completion, msecs_to_jiffies(1000))) + rc = -EINVAL; + + return rc; +} + +static int phytium_sgpio_direction_input(struct gpio_chip *gc, unsigned int offset) +{ + return phytium_sgpio_is_input(offset) ? 0 : -EINVAL; +} + +static int phytium_sgpio_direction_output(struct gpio_chip *gc, unsigned int offset, int val) +{ + struct phytium_sgpio *gpio = gpiochip_get_data(gc); + int rc; + + mutex_lock(&gpio->lock); + + /* + * No special action is required for setting the direction; we'll + * error-out in sgpio_set_value if this isn't an output GPIO + */ + rc = sgpio_set_value(&gpio->gc, offset, val); + + mutex_unlock(&gpio->lock); + + return rc; +} + +static int phytium_sgpio_get_direction(struct gpio_chip *gc, unsigned int offset) +{ + return !!phytium_sgpio_is_input(offset); +} + +static int phytium_sgpio_get(struct gpio_chip *gc, unsigned int offset) +{ + struct phytium_sgpio *gpio = gpiochip_get_data(gc); + int rc = 0; + u32 val, ctl0; + + mutex_lock(&gpio->lock); + + if (!phytium_sgpio_is_input(offset)) { + val = readl(gpio->regs + SGPIO_WDATA_REG(GPIO_GROUP(offset))); + rc = !!(val & GPIO_BIT(offset)); + mutex_unlock(&gpio->lock); + return rc; + } + + reinit_completion(&gpio->completion); + + /* Enable Rx */ + ctl0 = readl(gpio->regs + SGPIO_CTL0_REG); + writel(ctl0 & ~SGPIO_CTL0_REG_RX_DISABLE, gpio->regs + SGPIO_CTL0_REG); + + /* Start reading transaction and wait for completion */ + writel(readl(gpio->regs + SGPIO_CTL1_REG) | SGPIO_CTL1_REG_W_UPDATA, + gpio->regs + SGPIO_CTL1_REG); + if (!wait_for_completion_timeout(&gpio->completion, msecs_to_jiffies(1000))) { + rc = -EINVAL; + goto err; + } + + val = readl(gpio->regs + SGPIO_RDATA_REG(GPIO_GROUP(offset))); + rc = !!(val & GPIO_BIT(offset)); + +err: + /* Disalbe Rx to hold the value */ + writel(ctl0 | SGPIO_CTL0_REG_RX_DISABLE, gpio->regs + SGPIO_CTL0_REG); + mutex_unlock(&gpio->lock); + + return rc; +} + +static void phytium_sgpio_set(struct gpio_chip *gc, unsigned int offset, int val) +{ + struct phytium_sgpio *gpio = gpiochip_get_data(gc); + + mutex_lock(&gpio->lock); + + sgpio_set_value(gc, offset, val); + + mutex_unlock(&gpio->lock); +} + +static irqreturn_t phytium_sgpio_irq_handler(int irq, void *data) +{ + struct phytium_sgpio *gpio = data; + + if (!readl(gpio->regs + SGPIO_IRQ_REG)) + return IRQ_NONE; + + /* Clear the interrupt */ + writel(0, gpio->regs + SGPIO_IRQ_REG); + + /* Check if tx/rx has been done */ + if (!(readl(gpio->regs + SGPIO_CTL1_REG) & SGPIO_CTL1_REG_OP_STATE)) + complete(&gpio->completion); + + return IRQ_HANDLED; +} + +static int phytium_sgpio_probe(struct platform_device *pdev) +{ + u32 pclk_freq, sclk_freq, clk_div; + struct phytium_sgpio *gpio; + struct resource *res; + struct device *dev = &pdev->dev; + int rc; + + gpio = devm_kzalloc(dev, sizeof(*gpio), GFP_KERNEL); + if (!gpio) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + gpio->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(gpio->regs)) + return PTR_ERR(gpio->regs); + + if (devm_request_irq(dev, platform_get_irq(pdev, 0), + phytium_sgpio_irq_handler, + IRQF_SHARED, dev_name(dev), gpio)) { + dev_err(dev, "failed to request IRQ\n"); + return -ENOENT; + } + + rc = fwnode_property_read_u32(dev_fwnode(dev), "ngpios", &gpio->ngpios); + if (rc < 0) { + dev_err(dev, "Could not read ngpios property\n"); + return -EINVAL; + } else if (gpio->ngpios % 32) { + dev_err(&pdev->dev, "Number of GPIOs not multiple of 32: %d\n", + gpio->ngpios); + return -EINVAL; + } + + rc = fwnode_property_read_u32(dev_fwnode(dev), "bus-frequency", &sclk_freq); + if (rc < 0) { + dev_err(dev, "Could not read bus-frequency property\n"); + return -EINVAL; + } + + gpio->pclk = devm_clk_get(dev, NULL); + if (IS_ERR(gpio->pclk)) { + dev_err(dev, "Could not get the APB clock property\n"); + return PTR_ERR(gpio->pclk); + } + rc = clk_prepare_enable(gpio->pclk); + if (rc) { + dev_err(dev, "failed to enable pclk: %d\n", rc); + return rc; + } + pclk_freq = clk_get_rate(gpio->pclk); + + /* + * From the datasheet: + * (pclk / 2) / (clk_div + 1) = sclk + */ + if (sclk_freq == 0) { + dev_err(dev, "SCLK should not be 0\n"); + return -EINVAL; + } + + clk_div = (pclk_freq / (sclk_freq * 2)) - 1; + if (clk_div > (1 << 20) - 1) { + dev_err(dev, "clk_div is overflow\n"); + return -EINVAL; + } + + writel(FIELD_PREP(SGPIO_CTL0_REG_CLK_DIV_NUM, clk_div) | + FIELD_PREP(SGPIO_CTL0_REG_L3_L0, DEFAULT_L3_L0) | + SGPIO_CTL0_REG_RX_DISABLE | SGPIO_CTL0_REG_ENABLE, + gpio->regs + SGPIO_CTL0_REG); + + writel(FIELD_PREP(SGPIO_CTL1_REG_BIT_NUM, gpio->ngpios) | + SGPIO_CTL1_REG_READY, gpio->regs + SGPIO_CTL1_REG); + + mutex_init(&gpio->lock); + init_completion(&gpio->completion); + platform_set_drvdata(pdev, gpio); + + gpio->gc.parent = dev; + gpio->gc.base = -1; + gpio->gc.ngpio = gpio->ngpios * 2; + gpio->gc.label = dev_name(dev); + gpio->gc.direction_input = phytium_sgpio_direction_input; + gpio->gc.direction_output = phytium_sgpio_direction_output; + gpio->gc.get_direction = phytium_sgpio_get_direction; + gpio->gc.get = phytium_sgpio_get; + gpio->gc.set = phytium_sgpio_set; + + return devm_gpiochip_add_data(dev, &gpio->gc, gpio); +} + +static const struct of_device_id phytium_sgpio_of_match[] = { + { .compatible = "phytium,sgpio", }, + { } +}; +MODULE_DEVICE_TABLE(of, phytium_sgpio_of_match); + +static struct platform_driver phytium_sgpio_driver = { + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = of_match_ptr(phytium_sgpio_of_match), + }, + .probe = phytium_sgpio_probe, +}; +module_platform_driver(phytium_sgpio_driver); + +MODULE_AUTHOR("Chen Baozi "); +MODULE_DESCRIPTION("Phytium SGPIO driver"); +MODULE_LICENSE("GPL"); diff --git a/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/Kconfig b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/Kconfig new file mode 100644 index 000000000..e3024feb6 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/Kconfig @@ -0,0 +1,7 @@ +config DRM_PHYTIUM + tristate "DRM Support for Phytium Graphics Card" + depends on DRM + select DRM_KMS_HELPER + help + Choose this option if you have a phytium graphics card. + This driver provides kernel mode setting and buffer management to userspace. diff --git a/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/Makefile b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/Makefile new file mode 100644 index 000000000..2dc7ea111 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/Makefile @@ -0,0 +1,18 @@ +phytium-dc-drm-y := phytium_display_drv.o \ + phytium_plane.o \ + phytium_crtc.o \ + phytium_dp.o \ + phytium_fb.o \ + phytium_gem.o \ + phytium_fbdev.o \ + phytium_debugfs.o \ + px210_dp.o \ + phytium_panel.o \ + px210_dc.o \ + phytium_pci.o \ + pe220x_dp.o \ + pe220x_dc.o \ + phytium_platform.o + +obj-$(CONFIG_DRM_PHYTIUM) += phytium-dc-drm.o +CFLAGS_REMOVE_phytium_crtc.o += -mgeneral-regs-only diff --git a/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/pe220x_dc.c b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/pe220x_dc.c new file mode 100644 index 000000000..6bb18f0fd --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/pe220x_dc.c @@ -0,0 +1,255 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Phytium Pe220x display controller DRM driver + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include "phytium_display_drv.h" +#include "pe220x_reg.h" +#include "phytium_crtc.h" +#include "phytium_plane.h" +#include "phytium_fb.h" +#include "phytium_gem.h" + +void pe220x_dc_hw_disable(struct drm_crtc *crtc); + +static const unsigned int pe220x_primary_formats[] = { + DRM_FORMAT_ARGB2101010, + DRM_FORMAT_ABGR2101010, + DRM_FORMAT_RGBA1010102, + DRM_FORMAT_BGRA1010102, + DRM_FORMAT_ARGB8888, + DRM_FORMAT_ABGR8888, + DRM_FORMAT_RGBA8888, + DRM_FORMAT_BGRA8888, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_XBGR8888, + DRM_FORMAT_RGBX8888, + DRM_FORMAT_BGRX8888, + DRM_FORMAT_ARGB4444, + DRM_FORMAT_ABGR4444, + DRM_FORMAT_RGBA4444, + DRM_FORMAT_BGRA4444, + DRM_FORMAT_XRGB4444, + DRM_FORMAT_XBGR4444, + DRM_FORMAT_RGBX4444, + DRM_FORMAT_BGRX4444, + DRM_FORMAT_ARGB1555, + DRM_FORMAT_ABGR1555, + DRM_FORMAT_RGBA5551, + DRM_FORMAT_BGRA5551, + DRM_FORMAT_XRGB1555, + DRM_FORMAT_XBGR1555, + DRM_FORMAT_RGBX5551, + DRM_FORMAT_BGRX5551, + DRM_FORMAT_RGB565, + DRM_FORMAT_BGR565, + DRM_FORMAT_YUYV, + DRM_FORMAT_UYVY, + DRM_FORMAT_NV16, + DRM_FORMAT_NV12, + DRM_FORMAT_NV21, +}; + +static uint64_t pe220x_primary_formats_modifiers[] = { + DRM_FORMAT_MOD_LINEAR, + DRM_FORMAT_MOD_INVALID +}; + +static uint64_t pe220x_cursor_formats_modifiers[] = { + DRM_FORMAT_MOD_LINEAR, + DRM_FORMAT_MOD_INVALID +}; + +static const unsigned int pe220x_cursor_formats[] = { + DRM_FORMAT_ARGB8888, +}; + +void pe220x_dc_hw_vram_init(struct phytium_display_private *priv, resource_size_t vram_addr, + resource_size_t vram_size) +{ + uint32_t config; + uint32_t group_offset = priv->address_transform_base; + + phytium_writel_reg(priv, (vram_addr & SRC_ADDR_MASK) >> SRC_ADDR_OFFSET, + group_offset, PE220X_DC_ADDRESS_TRANSFORM_SRC_ADDR); + phytium_writel_reg(priv, (vram_size >> SIZE_OFFSET) | ADDRESS_TRANSFORM_ENABLE, + group_offset, PE220X_DC_ADDRESS_TRANSFORM_SIZE); + config = phytium_readl_reg(priv, group_offset, PE220X_DC_ADDRESS_TRANSFORM_DST_ADDR); + phytium_writel_reg(priv, config, group_offset, PE220X_DC_ADDRESS_TRANSFORM_DST_ADDR); +} + +void pe220x_dc_hw_config_pix_clock(struct drm_crtc *crtc, int clock) +{ + struct drm_device *dev = crtc->dev; + struct phytium_display_private *priv = dev->dev_private; + struct phytium_crtc *phytium_crtc = to_phytium_crtc(crtc); + int phys_pipe = phytium_crtc->phys_pipe; + int ret = 0; + + /* config pix clock */ + phytium_writel_reg(priv, FLAG_REQUEST | CMD_PIXEL_CLOCK | (clock & PIXEL_CLOCK_MASK), + 0, PE220X_DC_CMD_REGISTER(phys_pipe)); + ret = phytium_wait_cmd_done(priv, PE220X_DC_CMD_REGISTER(phys_pipe), + FLAG_REQUEST, FLAG_REPLY); + if (ret < 0) + DRM_ERROR("%s: failed to set pixel clock\n", __func__); +} + +void pe220x_dc_hw_reset(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct phytium_display_private *priv = dev->dev_private; + struct phytium_crtc *phytium_crtc = to_phytium_crtc(crtc); + int config = 0; + int phys_pipe = phytium_crtc->phys_pipe; + + /* disable pixel clock for bmc mode */ + if (phys_pipe == 0) + pe220x_dc_hw_disable(crtc); + + config = phytium_readl_reg(priv, 0, PE220X_DC_CLOCK_CONTROL); + config &= (~(DC0_CORE_RESET | DC1_CORE_RESET | AXI_RESET | AHB_RESET)); + + if (phys_pipe == 0) { + phytium_writel_reg(priv, config | DC0_CORE_RESET, + 0, PE220X_DC_CLOCK_CONTROL); + udelay(20); + phytium_writel_reg(priv, config | DC0_CORE_RESET | AXI_RESET, + 0, PE220X_DC_CLOCK_CONTROL); + udelay(20); + phytium_writel_reg(priv, config | DC0_CORE_RESET | AXI_RESET | AHB_RESET, + 0, PE220X_DC_CLOCK_CONTROL); + udelay(20); + phytium_writel_reg(priv, config | DC0_CORE_RESET | AXI_RESET, + 0, PE220X_DC_CLOCK_CONTROL); + udelay(20); + phytium_writel_reg(priv, config | DC0_CORE_RESET, + 0, PE220X_DC_CLOCK_CONTROL); + udelay(20); + phytium_writel_reg(priv, config, 0, PE220X_DC_CLOCK_CONTROL); + udelay(20); + } else { + phytium_writel_reg(priv, config | DC1_CORE_RESET, + 0, PE220X_DC_CLOCK_CONTROL); + udelay(20); + phytium_writel_reg(priv, config | DC1_CORE_RESET | AXI_RESET, + 0, PE220X_DC_CLOCK_CONTROL); + udelay(20); + phytium_writel_reg(priv, config | DC1_CORE_RESET | AXI_RESET | AHB_RESET, + 0, PE220X_DC_CLOCK_CONTROL); + udelay(20); + phytium_writel_reg(priv, config | DC1_CORE_RESET | AXI_RESET, + 0, PE220X_DC_CLOCK_CONTROL); + udelay(20); + phytium_writel_reg(priv, config | DC1_CORE_RESET, + 0, PE220X_DC_CLOCK_CONTROL); + udelay(20); + phytium_writel_reg(priv, config, 0, PE220X_DC_CLOCK_CONTROL); + udelay(20); + } +} + +void pe220x_dc_hw_disable(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct phytium_display_private *priv = dev->dev_private; + struct phytium_crtc *phytium_crtc = to_phytium_crtc(crtc); + int config = 0; + int phys_pipe = phytium_crtc->phys_pipe; + + /* clear framebuffer */ + phytium_writel_reg(priv, CLEAR_VALUE_BLACK, priv->dc_reg_base[phys_pipe], + PHYTIUM_DC_FRAMEBUFFER_CLEARVALUE); + config = phytium_readl_reg(priv, priv->dc_reg_base[phys_pipe], + PHYTIUM_DC_FRAMEBUFFER_CONFIG); + config |= FRAMEBUFFER_CLEAR; + phytium_writel_reg(priv, config, priv->dc_reg_base[phys_pipe], + PHYTIUM_DC_FRAMEBUFFER_CONFIG); + + /* disable cursor */ + config = phytium_readl_reg(priv, priv->dc_reg_base[phys_pipe], PHYTIUM_DC_CURSOR_CONFIG); + config = ((config & (~CURSOR_FORMAT_MASK)) | CURSOR_FORMAT_DISABLED); + phytium_writel_reg(priv, config, priv->dc_reg_base[phys_pipe], PHYTIUM_DC_CURSOR_CONFIG); + mdelay(20); + + /* reset pix clock */ + pe220x_dc_hw_config_pix_clock(crtc, 0); + + if (phys_pipe == 0) { + config = phytium_readl_reg(priv, 0, PE220X_DC_CLOCK_CONTROL); + phytium_writel_reg(priv, config | DC0_CORE_RESET, 0, PE220X_DC_CLOCK_CONTROL); + udelay(20); + phytium_writel_reg(priv, config & (~DC0_CORE_RESET), 0, PE220X_DC_CLOCK_CONTROL); + } else { + config = phytium_readl_reg(priv, 0, PE220X_DC_CLOCK_CONTROL); + phytium_writel_reg(priv, config | DC1_CORE_RESET, 0, PE220X_DC_CLOCK_CONTROL); + udelay(20); + phytium_writel_reg(priv, config & (~DC1_CORE_RESET), 0, PE220X_DC_CLOCK_CONTROL); + } + udelay(20); +} + +int pe220x_dc_hw_fb_format_check(const struct drm_mode_fb_cmd2 *mode_cmd, int count) +{ + int ret = 0; + + if (mode_cmd->modifier[count] != DRM_FORMAT_MOD_LINEAR) { + DRM_ERROR("unsupported fb modifier 0x%llx\n", mode_cmd->modifier[count]); + ret = -EINVAL; + } + + return ret; +} + +void pe220x_dc_hw_plane_get_primary_format(const uint64_t **format_modifiers, + const uint32_t **formats, + uint32_t *format_count) +{ + *format_modifiers = pe220x_primary_formats_modifiers; + *formats = pe220x_primary_formats; + *format_count = ARRAY_SIZE(pe220x_primary_formats); +} + +void pe220x_dc_hw_plane_get_cursor_format(const uint64_t **format_modifiers, + const uint32_t **formats, + uint32_t *format_count) +{ + *format_modifiers = pe220x_cursor_formats_modifiers; + *formats = pe220x_cursor_formats; + *format_count = ARRAY_SIZE(pe220x_cursor_formats); +} + +void pe220x_dc_hw_update_primary_hi_addr(struct drm_plane *plane) +{ + struct drm_device *dev = plane->dev; + struct phytium_display_private *priv = dev->dev_private; + struct phytium_plane *phytium_plane = to_phytium_plane(plane); + int phys_pipe = phytium_plane->phys_pipe; + + phytium_writel_reg(priv, (phytium_plane->iova[0] >> PREFIX_SHIFT) & PREFIX_MASK, + priv->dc_reg_base[phys_pipe], PE220X_DC_FRAMEBUFFER_Y_HI_ADDRESS); + + phytium_writel_reg(priv, (phytium_plane->iova[1] >> U_PREFIX_SHIFT) & U_PREFIX_MASK, + priv->dc_reg_base[phys_pipe], PE220X_DC_FRAMEBUFFER_U_HI_ADDRESS); + + phytium_writel_reg(priv, (phytium_plane->iova[2] >> V_PREFIX_SHIFT) & V_PREFIX_MASK, + priv->dc_reg_base[phys_pipe], PE220X_DC_FRAMEBUFFER_V_HI_ADDRESS); +} + +void pe220x_dc_hw_update_cursor_hi_addr(struct drm_plane *plane, uint64_t iova) +{ + struct drm_device *dev = plane->dev; + struct phytium_display_private *priv = dev->dev_private; + struct phytium_plane *phytium_plane = to_phytium_plane(plane); + int phys_pipe = phytium_plane->phys_pipe; + int config; + + config = ((iova >> CURSOR_PREFIX_SHIFT) & CURSOR_PREFIX_MASK); + phytium_writel_reg(priv, config, priv->dc_reg_base[phys_pipe], PE220X_DC_CURSOR_HI_ADDRESS); +} diff --git a/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/pe220x_dc.h b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/pe220x_dc.h new file mode 100644 index 000000000..76b6700a4 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/pe220x_dc.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Phytium Pe220x display controller DRM driver + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#ifndef __PE220X_DC_H__ +#define __PE220X_DC_H__ + +#define PE220X_DC_PIX_CLOCK_MAX (594000) +#define PE220X_DC_HDISPLAY_MAX 3840 +#define PE220X_DC_VDISPLAY_MAX 2160 +#define PE220X_DC_ADDRESS_MASK 0x7f + +extern void pe220x_dc_hw_vram_init(struct phytium_display_private *priv, + resource_size_t vram_addr, + resource_size_t vram_size); +extern void pe220x_dc_hw_config_pix_clock(struct drm_crtc *crtc, int clock); +extern void pe220x_dc_hw_disable(struct drm_crtc *crtc); +extern int pe220x_dc_hw_fb_format_check(const struct drm_mode_fb_cmd2 *mode_cmd, int count); +extern void pe220x_dc_hw_plane_get_primary_format(const uint64_t **format_modifiers, + const uint32_t **formats, + uint32_t *format_count); +extern void pe220x_dc_hw_plane_get_cursor_format(const uint64_t **format_modifiers, + const uint32_t **formats, + uint32_t *format_count); +extern void pe220x_dc_hw_update_primary_hi_addr(struct drm_plane *plane); +extern void pe220x_dc_hw_update_cursor_hi_addr(struct drm_plane *plane, uint64_t iova); +void pe220x_dc_hw_reset(struct drm_crtc *crtc); +#endif /* __PE220X_DC_H__ */ diff --git a/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/pe220x_dp.c b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/pe220x_dp.c new file mode 100644 index 000000000..19f38fc01 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/pe220x_dp.c @@ -0,0 +1,514 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Phytium display port DRM driver + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#include "phytium_display_drv.h" +#include "pe220x_reg.h" +#include "phytium_dp.h" +#include "pe220x_dp.h" + +static uint8_t pe220x_dp_source_lane_count[2] = {1, 1}; + +/* [reg][ling_rate 1.62->8.1] */ +static int vco_val[12][4] = { + {0x0509, 0x0509, 0x0509, 0x0509}, /* CP_PADJ */ + {0x0f00, 0x0f00, 0x0f00, 0x0f00}, /* CP_IADJ */ + {0x0F08, 0x0F08, 0x0F08, 0x0F08}, /* FILT_PADJ */ + {0x0061, 0x006C, 0x006C, 0x0051}, /* INTDIV */ + {0x3333, 0x0000, 0x0000, 0x0000}, /* FRACDIVL */ + {0x0000, 0x0000, 0x0000, 0x0000}, /* FRACDIVH */ + {0x0042, 0x0048, 0x0048, 0x0036}, /* HIGH_THR */ + {0x0002, 0x0002, 0x0002, 0x0002}, /* PDIAG_CTRL */ + {0x0c5e, 0x0c5e, 0x0c5e, 0x0c5e}, /* VCOCAL_PLLCNT_START */ + {0x00c7, 0x00c7, 0x00c7, 0x00c7}, /* LOCK_PEFCNT */ + {0x00c7, 0x00c7, 0x00c7, 0x00c7}, /* LOCK_PLLCNT_START */ + {0x0005, 0x0005, 0x0005, 0x0005}, /* LOCK_PLLCNT_THR */ +}; + +/* [link_rate][swing][emphasis] */ +static int mgnfs_val[4][4][4] = { + /* 1.62Gbps */ + { + {0x0026, 0x001f, 0x0012, 0x0000}, + {0x0013, 0x0013, 0x0000, 0x0000}, + {0x0006, 0x0000, 0x0000, 0x0000}, + {0x0000, 0x0000, 0x0000, 0x0000}, + }, + /* 2.7Gbps */ + { + {0x0026, 0x001f, 0x0012, 0x0000}, + {0x0013, 0x0013, 0x0000, 0x0000}, + {0x0006, 0x0000, 0x0000, 0x0000}, + {0x0000, 0x0000, 0x0000, 0x0000}, + }, + /* 5.4Gbps */ + { + {0x001f, 0x0013, 0x005, 0x0000}, + {0x0018, 0x006, 0x0000, 0x0000}, + {0x000c, 0x0000, 0x0000, 0x0000}, + {0x0000, 0x0000, 0x0000, 0x0000}, + }, + /* 8.1Gbps */ + { + {0x0026, 0x0013, 0x005, 0x0000}, + {0x0013, 0x006, 0x0000, 0x0000}, + {0x0006, 0x0000, 0x0000, 0x0000}, + {0x0000, 0x0000, 0x0000, 0x0000}, + }, +}; + +/* [link_rate][swing][emphasis] */ +static int cpost_val[4][4][4] = { + /* 1.62Gbps */ + { + {0x0000, 0x0014, 0x0020, 0x002a}, + {0x0000, 0x0010, 0x001f, 0x0000}, + {0x0000, 0x0013, 0x0000, 0x0000}, + {0x0000, 0x0000, 0x0000, 0x0000}, + }, + /* 2.7Gbps */ + { + {0x0000, 0x0014, 0x0020, 0x002a}, + {0x0000, 0x0010, 0x001f, 0x0000}, + {0x0000, 0x0013, 0x0000, 0x0000}, + {0x0000, 0x0000, 0x0000, 0x0000}, + }, + /* 5.4Gbps */ + { + {0x0005, 0x0014, 0x0022, 0x002e}, + {0x0000, 0x0013, 0x0020, 0x0000}, + {0x0000, 0x0013, 0x0000, 0x0000}, + {0x0000, 0x0000, 0x0000, 0x0000}, + }, + /* 8.1Gbps */ + { + {0x0000, 0x0014, 0x0022, 0x002e}, + {0x0000, 0x0013, 0x0020, 0x0000}, + {0x0000, 0x0013, 0x0000, 0x0000}, + {0x0000, 0x0000, 0x0000, 0x0000}, + }, +}; + +static int pe220x_dp_hw_set_phy_lane_and_rate(struct phytium_dp_device *phytium_dp, + uint8_t link_lane_count, uint32_t link_rate) +{ + int port = phytium_dp->port%2; + int i = 0, data, tmp, tmp1, index = 0, mask = 0; + int timeout = 500, ret = 0; + + /* set pma powerdown */ + data = 0; + for (i = 0; i < phytium_dp->source_max_lane_count; i++) + data |= (A3_POWERDOWN3 << (i * A3_POWERDOWN3_SHIFT)); + phytium_phy_writel(phytium_dp, PE220X_PHY_PMA0_POWER(port), data); + + /* lane pll disable */ + data = 0; + for (i = 0; i < phytium_dp->source_max_lane_count; i++) { + data |= (PLL_EN << (i * PLL_EN_SHIFT)); + mask |= (((1<source_max_lane_count; i++) + data |= (PLL_EN << (i * PLL_EN_SHIFT)); + phytium_phy_writel(phytium_dp, PE220X_PHY_PLL_EN(port), data); + + /* set pma power active */ + data = 0; + for (i = 0; i < phytium_dp->source_max_lane_count; i++) + data |= (A0_ACTIVE << (i * A0_ACTIVE_SHIFT)); + phytium_phy_writel(phytium_dp, PE220X_PHY_PMA0_POWER(port), data); + + mask = PLL0_LOCK_DONE; + do { + mdelay(1); + timeout--; + tmp = phytium_phy_readl(phytium_dp, PE220X_PHY_PMA_CONTROL2(port)); + } while ((!(tmp & mask)) && timeout); + + if (timeout == 0) { + DRM_ERROR("dp(%d) phy pll lock failed\n", port); + ret = -1; + } + udelay(1); + + return ret; +} + +static void pe220x_dp_hw_set_phy_lane_setting(struct phytium_dp_device *phytium_dp, + uint32_t link_rate, uint8_t train_set) +{ + int port = phytium_dp->port % 3; + int voltage_swing = 0; + int pre_emphasis = 0, link_rate_index = 0; + + switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) { + case DP_TRAIN_VOLTAGE_SWING_LEVEL_1: + voltage_swing = 1; + break; + case DP_TRAIN_VOLTAGE_SWING_LEVEL_2: + voltage_swing = 2; + break; + case DP_TRAIN_VOLTAGE_SWING_LEVEL_3: + voltage_swing = 3; + break; + default: + voltage_swing = 0; + break; + } + + switch (train_set & DP_TRAIN_PRE_EMPHASIS_MASK) { + case DP_TRAIN_PRE_EMPH_LEVEL_1: + pre_emphasis = 1; + break; + case DP_TRAIN_PRE_EMPH_LEVEL_2: + pre_emphasis = 2; + break; + case DP_TRAIN_PRE_EMPH_LEVEL_3: + pre_emphasis = 3; + break; + default: + pre_emphasis = 0; + break; + } + + switch (link_rate) { + case 810000: + link_rate_index = 3; + break; + case 540000: + link_rate_index = 2; + break; + case 270000: + link_rate_index = 1; + break; + case 162000: + link_rate_index = 0; + break; + default: + DRM_ERROR("phytium dp rate(%d) not support\n", link_rate); + link_rate_index = 2; + break; + } + + phytium_phy_writel(phytium_dp, PE220X_PHY_PLL0_TX_DIAG_ACYA(port), LOCK); + phytium_phy_writel(phytium_dp, PE220X_PHY_PLL0_TX_TXCC_CTRL(port), TX_TXCC_CTRL); + phytium_phy_writel(phytium_dp, PE220X_PHY_PLL0_TX_DRV(port), TX_DRV); + phytium_phy_writel(phytium_dp, PE220X_PHY_PLL0_TX_MGNFS(port), + mgnfs_val[link_rate_index][voltage_swing][pre_emphasis]); + phytium_phy_writel(phytium_dp, PE220X_PHY_PLL0_TX_CPOST(port), + cpost_val[link_rate_index][voltage_swing][pre_emphasis]); + phytium_phy_writel(phytium_dp, PE220X_PHY_PLL0_TX_DIAG_ACYA(port), UNLOCK); +} + +static int pe220x_dp_hw_init_phy(struct phytium_dp_device *phytium_dp) +{ + int port = phytium_dp->port; + int i = 0, data, tmp, mask; + int timeout = 500, ret = 0; + + phytium_phy_writel(phytium_dp, PE220X_PHY_APB_RESET(port), APB_RESET); + phytium_phy_writel(phytium_dp, PE220X_PHY_PIPE_RESET(port), RESET); + + /* config lane to dp mode */ + data = 0; + for (i = 0; i < phytium_dp->source_max_lane_count; i++) + data |= (LANE_BIT << (i * LANE_BIT_SHIFT)); + phytium_phy_writel(phytium_dp, PE220X_PHY_MODE(port), data); + + /* pll clock enable */ + data = 0; + for (i = 0; i < phytium_dp->source_max_lane_count; i++) + data |= (PLL_EN << (i * PLL_EN_SHIFT)); + phytium_phy_writel(phytium_dp, PE220X_PHY_PLL_EN(port), data); + + /* config input 20 bit */ + data = 0; + for (i = 0; i < phytium_dp->source_max_lane_count; i++) + data |= (BIT_20 << (i * BIT_20_SHIFT)); + phytium_phy_writel(phytium_dp, PE220X_PHY_PMA_WIDTH(port), data); + + /* config lane active power state */ + data = 0; + for (i = 0; i < phytium_dp->source_max_lane_count; i++) + data |= (A0_ACTIVE << (i * A0_ACTIVE_SHIFT)); + phytium_phy_writel(phytium_dp, PE220X_PHY_PMA0_POWER(port), data); + + /* link reset */ + phytium_phy_writel(phytium_dp, PE220X_PHY_LINK_RESET(port), LINK_RESET); + + phytium_phy_writel(phytium_dp, PE220X_PHY_SGMII_DPSEL_INIT(port), DP_SEL); + + /* config single link */ + phytium_phy_writel(phytium_dp, PE220X_PHY_PLL_CFG(port), SINGLE_LINK); + + /* pipe reset */ + phytium_phy_writel(phytium_dp, PE220X_PHY_PIPE_RESET(port), RESET_DEASSERT); + + mask = PLL0_LOCK_DONE; + do { + mdelay(1); + timeout--; + tmp = phytium_phy_readl(phytium_dp, PE220X_PHY_PMA_CONTROL2(port)); + } while ((!(tmp & mask)) && timeout); + + if (timeout == 0) { + DRM_ERROR("reset dp(%d) phy failed\n", port); + ret = -1; + } + udelay(1); + + return ret; +} + +static void pe220x_dp_hw_poweron_panel(struct phytium_dp_device *phytium_dp) +{ + struct drm_device *dev = phytium_dp->dev; + struct phytium_display_private *priv = dev->dev_private; + int port = phytium_dp->port; + int ret = 0; + + phytium_writel_reg(priv, FLAG_REQUEST | CMD_BACKLIGHT | PANEL_POWER_ENABLE, + 0, PE220X_DC_CMD_REGISTER(port)); + ret = phytium_wait_cmd_done(priv, PE220X_DC_CMD_REGISTER(port), + FLAG_REQUEST, FLAG_REPLY); + if (ret < 0) + DRM_ERROR("%s: failed to poweron panel\n", __func__); +} + +static void pe220x_dp_hw_poweroff_panel(struct phytium_dp_device *phytium_dp) +{ + struct drm_device *dev = phytium_dp->dev; + struct phytium_display_private *priv = dev->dev_private; + int port = phytium_dp->port; + int ret = 0; + + phytium_writel_reg(priv, FLAG_REQUEST | CMD_BACKLIGHT | PANEL_POWER_DISABLE, + 0, PE220X_DC_CMD_REGISTER(port)); + ret = phytium_wait_cmd_done(priv, PE220X_DC_CMD_REGISTER(port), + FLAG_REQUEST, FLAG_REPLY); + if (ret < 0) + DRM_ERROR("%s: failed to poweroff panel\n", __func__); +} + +static void pe220x_dp_hw_enable_backlight(struct phytium_dp_device *phytium_dp) +{ + struct drm_device *dev = phytium_dp->dev; + struct phytium_display_private *priv = dev->dev_private; + int port = phytium_dp->port, ret = 0; + + phytium_writel_reg(priv, FLAG_REQUEST | CMD_BACKLIGHT | BACKLIGHT_ENABLE, + 0, PE220X_DC_CMD_REGISTER(port)); + ret = phytium_wait_cmd_done(priv, PE220X_DC_CMD_REGISTER(port), + FLAG_REQUEST, FLAG_REPLY); + if (ret < 0) + DRM_ERROR("%s: failed to enable backlight\n", __func__); +} + +static void pe220x_dp_hw_disable_backlight(struct phytium_dp_device *phytium_dp) +{ + struct drm_device *dev = phytium_dp->dev; + struct phytium_display_private *priv = dev->dev_private; + int port = phytium_dp->port; + int ret = 0; + + phytium_writel_reg(priv, FLAG_REQUEST | CMD_BACKLIGHT | BACKLIGHT_DISABLE, + 0, PE220X_DC_CMD_REGISTER(port)); + ret = phytium_wait_cmd_done(priv, PE220X_DC_CMD_REGISTER(port), + FLAG_REQUEST, FLAG_REPLY); + if (ret < 0) + DRM_ERROR("%s: failed to disable backlight\n", __func__); +} + +static uint32_t pe220x_dp_hw_get_backlight(struct phytium_dp_device *phytium_dp) +{ + struct drm_device *dev = phytium_dp->dev; + struct phytium_display_private *priv = dev->dev_private; + int config; + uint32_t group_offset = priv->address_transform_base; + + config = phytium_readl_reg(priv, group_offset, PE220X_DC_ADDRESS_TRANSFORM_BACKLIGHT_VALUE); + return ((config >> BACKLIGHT_VALUE_SHIFT) & BACKLIGHT_VALUE_MASK); +} + +static int pe220x_dp_hw_set_backlight(struct phytium_dp_device *phytium_dp, uint32_t level) +{ + struct drm_device *dev = phytium_dp->dev; + struct phytium_display_private *priv = dev->dev_private; + int port = phytium_dp->port; + int config = 0; + int ret = 0; + + if (level > PE220X_DP_BACKLIGHT_MAX) { + ret = -EINVAL; + goto out; + } + + config = FLAG_REQUEST | CMD_BACKLIGHT | ((level & BACKLIGHT_MASK) << BACKLIGHT_SHIFT); + phytium_writel_reg(priv, config, 0, PE220X_DC_CMD_REGISTER(port)); + ret = phytium_wait_cmd_done(priv, PE220X_DC_CMD_REGISTER(port), + FLAG_REQUEST, FLAG_REPLY); + if (ret < 0) + DRM_ERROR("%s: failed to set backlight\n", __func__); +out: + return ret; +} + +bool pe220x_dp_hw_spread_is_enable(struct phytium_dp_device *phytium_dp) +{ + return false; +} + +int pe220x_dp_hw_reset(struct phytium_dp_device *phytium_dp) +{ + struct drm_device *dev = phytium_dp->dev; + struct phytium_display_private *priv = dev->dev_private; + int port = phytium_dp->port; + uint32_t group_offset = priv->dp_reg_base[port]; + + phytium_writel_reg(priv, DP_RESET, group_offset, PE220X_DP_CONTROLLER_RESET); + udelay(500); + phytium_writel_reg(priv, AUX_CLK_DIVIDER_100, group_offset, PHYTIUM_DP_AUX_CLK_DIVIDER); + phytium_writel_reg(priv, SUPPORT_EDP_1_4, group_offset, PHYTIUM_EDP_CRC_ENABLE); + + return 0; +} + +uint8_t pe220x_dp_hw_get_source_lane_count(struct phytium_dp_device *phytium_dp) +{ + return pe220x_dp_source_lane_count[phytium_dp->port]; +} + +static struct phytium_dp_func pe220x_dp_funcs = { + .dp_hw_get_source_lane_count = pe220x_dp_hw_get_source_lane_count, + .dp_hw_reset = pe220x_dp_hw_reset, + .dp_hw_spread_is_enable = pe220x_dp_hw_spread_is_enable, + .dp_hw_set_backlight = pe220x_dp_hw_set_backlight, + .dp_hw_get_backlight = pe220x_dp_hw_get_backlight, + .dp_hw_disable_backlight = pe220x_dp_hw_disable_backlight, + .dp_hw_enable_backlight = pe220x_dp_hw_enable_backlight, + .dp_hw_poweroff_panel = pe220x_dp_hw_poweroff_panel, + .dp_hw_poweron_panel = pe220x_dp_hw_poweron_panel, + .dp_hw_init_phy = pe220x_dp_hw_init_phy, + .dp_hw_set_phy_lane_setting = pe220x_dp_hw_set_phy_lane_setting, + .dp_hw_set_phy_lane_and_rate = pe220x_dp_hw_set_phy_lane_and_rate, +}; + +void pe220x_dp_func_register(struct phytium_dp_device *phytium_dp) +{ + phytium_dp->funcs = &pe220x_dp_funcs; +} diff --git a/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/pe220x_dp.h b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/pe220x_dp.h new file mode 100644 index 000000000..a79bf5b5e --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/pe220x_dp.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Phytium display port DRM driver + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#ifndef __PE220X_DP_H__ +#define __PE220X_DP_H__ + +#define PE220X_DP_BACKLIGHT_MAX 100 + +void pe220x_dp_func_register(struct phytium_dp_device *phytium_dp); +#endif /* __PE220X_DP_H__ */ diff --git a/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/pe220x_reg.h b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/pe220x_reg.h new file mode 100644 index 000000000..8dfbd41a5 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/pe220x_reg.h @@ -0,0 +1,209 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Phytium Pe220x display engine register + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#ifndef __PE220X_REG_H__ +#define __PE220X_REG_H__ + +#include "phytium_reg.h" + +/* dc register */ +#define PE220X_DC_CLOCK_CONTROL 0x0000 +#define DC1_CORE_RESET (1<<18) +#define DC0_CORE_RESET (1<<17) +#define AXI_RESET (1<<16) +#define AHB_RESET (1<<12) + +#define PE220X_DC_CMD_REGISTER(pipe) (PE220X_DC_BASE(0) + 0x00F0 + 0x4*(pipe)) +#define FLAG_REPLY (1<<31) +#define FLAG_REQUEST (1<<30) +#define CMD_PIXEL_CLOCK (0x0 << 28) +#define CMD_BACKLIGHT (0x1 << 28) +#define CMD_DC_DP_RESET (0x3 << 28) +#define BACKLIGHT_SHIFT 21 +#define BACKLIGHT_MASK 0x7f +#define BACKLIGHT_MAX 100 +#define BACKLIGHT_ENABLE (101 << BACKLIGHT_SHIFT) +#define BACKLIGHT_DISABLE (102 << BACKLIGHT_SHIFT) +#define PANEL_POWER_ENABLE (103 << BACKLIGHT_SHIFT) +#define PANEL_POWER_DISABLE (104 << BACKLIGHT_SHIFT) +#define PIXEL_CLOCK_MASK (0x1fffff) + +#define PE220X_DC_FRAMEBUFFER_Y_HI_ADDRESS 0x1404 +#define PREFIX_MASK 0xff +#define PREFIX_SHIFT 32 + +#define PE220X_DC_CURSOR_HI_ADDRESS 0x1490 +#define CURSOR_PREFIX_MASK 0xff +#define CURSOR_PREFIX_SHIFT 32 + +#define PE220X_DC_FRAMEBUFFER_U_HI_ADDRESS 0x1534 +#define U_PREFIX_MASK 0xff +#define U_PREFIX_SHIFT 32 + +#define PE220X_DC_FRAMEBUFFER_V_HI_ADDRESS 0x153c +#define V_PREFIX_MASK 0xff +#define V_PREFIX_SHIFT 32 + +/* dp register */ +#define PE220X_DP_CONTROLLER_RESET 0x0850 +#define DP_RESET 0x1 + +/* address transform register */ +#define PE220X_DC_ADDRESS_TRANSFORM_SRC_ADDR 0x0 +#define SRC_ADDR_OFFSET 22 +#define SRC_ADDR_MASK 0xffffffffff + +#define PE220X_DC_ADDRESS_TRANSFORM_SIZE 0x4 +#define ADDRESS_TRANSFORM_ENABLE (0x1 << 31) +#define SIZE_OFFSET 22 + +#define PE220X_DC_ADDRESS_TRANSFORM_DST_ADDR 0x8 +#define DST_ADDR_OFFSET 22 + +#define PE220X_DC_ADDRESS_TRANSFORM_DP_RESET_STATUS 0x48 +#define DC_DP_RESET_STATUS(pipe) (1 << pipe) +#define DP_SPREAD_ENABLE(pipe) (0x8 << pipe) + +#define PE220X_DC_ADDRESS_TRANSFORM_BACKLIGHT_VALUE 0x4c +#define BACKLIGHT_VALUE_MASK (0x7f) +#define BACKLIGHT_VALUE_SHIFT 16 + +/* phy register start */ +#define PE220X_PHY_BASE(pipe) (0x100000*pipe) + +#define PE220X_PHY_PIPE_RESET(pipe) (PE220X_PHY_BASE(pipe) + 0x40254) +#define RESET 0x0 +#define RESET_DEASSERT 0x1 + +#define PE220X_PHY_MODE(pipe) (PE220X_PHY_BASE(pipe) + 0x40034) +#define LANE_BIT (0x3) +#define LANE_BIT_SHIFT 0x2 + +#define PE220X_PHY_LINK_CFG(pipe) (PE220X_PHY_BASE(pipe) + 0x40044) +#define LANE_MASTER 0x1 +#define LANE_MASTER_SHIFT 1 + +#define PE220X_PHY_PLL_EN(pipe) (PE220X_PHY_BASE(pipe) + 0x40214) +#define PLL_EN 0x1 +#define PLL_EN_SHIFT 1 + +#define PE220X_PHY_PMA_WIDTH(pipe) (PE220X_PHY_BASE(pipe) + 0x4021c) +#define BIT_20 0x5 +#define BIT_20_SHIFT 4 + +#define PE220X_PHY_PLL_SOURCE_SEL(pipe) (PE220X_PHY_BASE(pipe) + 0x4004C) + +#define PE220X_PHY_PMA0_POWER(pipe) (PE220X_PHY_BASE(pipe) + 0x402bc) +#define A0_ACTIVE 0x1 +#define A0_ACTIVE_SHIFT 8 +#define A3_POWERDOWN3 0x8 +#define A3_POWERDOWN3_SHIFT 8 + +#define PE220X_PHY_LINK_RESET(pipe) (PE220X_PHY_BASE(pipe) + 0x40258) +#define LINK_RESET 0x1 +#define LINK_RESET_MASK 0x1 +#define LINTK_RESET_SHIFT 0x1 + +#define PE220X_PHY_SGMII_DPSEL_INIT(pipe) (PE220X_PHY_BASE(pipe) + 0x40260) +#define DP_SEL 0x1 + +#define PE220X_PHY_APB_RESET(pipe) (PE220X_PHY_BASE(pipe) + 0x40250) +#define APB_RESET 0x1 + +/* phy origin register */ +#define PE220X_PHY_PLL_CFG(pipe) (PE220X_PHY_BASE(pipe) + 0x30038) +#define SINGLE_LINK 0x0 + +#define PE220X_PHY_PMA_CONTROL(pipe) (PE220X_PHY_BASE(pipe) + 0x3800c) +#define CONTROL_ENABLE 0x1 +#define CONTROL_ENABLE_MASK 0x1 +#define CONTROL_ENABLE_SHIFT 0x1 + +#define PE220X_PHY_PMA_CONTROL2(pipe) (PE220X_PHY_BASE(pipe) + 0x38004) +#define PLL0_LOCK_DONE (0x1 << 6) + +#define PE220X_PHY_PLL0_CLK_SEL(pipe) (PE220X_PHY_BASE(pipe) + 0X684) +#define PLL_LINK_RATE_162000 0xf01 +#define PLL_LINK_RATE_270000 0x701 +#define PLL_LINK_RATE_540000 0x301 +#define PLL_LINK_RATE_810000 0x200 + +#define PE220X_PHY_HSCLK0_SEL(pipe) (PE220X_PHY_BASE(pipe) + 0x18398) +#define HSCLK_LINK_0 0x0 +#define HSCLK_LINK_1 0x1 + +#define PE220X_PHY_HSCLK0_DIV(pipe) (PE220X_PHY_BASE(pipe) + 0x1839c) +#define HSCLK_LINK_RATE_162000 0x2 +#define HSCLK_LINK_RATE_270000 0x1 +#define HSCLK_LINK_RATE_540000 0x0 +#define HSCLK_LINK_RATE_810000 0x0 + +#define PE220X_PHY_PLLDRC0_CTRL(pipe) (PE220X_PHY_BASE(pipe) + 0x18394) +#define PLLDRC_LINK0 0x1 +#define PLLDRC_LINK1 0x9 + +#define PE220X_PHY_PLL0_DSM_M0(pipe) (PE220X_PHY_BASE(pipe) + 0x250) +#define PLL0_DSM_M0 0x4 +#define PE220X_PHY_PLL0_VCOCAL_START(pipe) (PE220X_PHY_BASE(pipe) + 0x218) +#define PLL0_VCOCAL_START 0xc5e +#define PE220X_PHY_PLL0_VCOCAL_CTRL(pipe) (PE220X_PHY_BASE(pipe) + 0x208) +#define PLL0_VCOCAL_CTRL 0x3 + +#define PE220X_PHY_PLL0_CP_PADJ(pipe) (PE220X_PHY_BASE(pipe) + 0x690) +#define PE220X_PHY_PLL0_CP_IADJ(pipe) (PE220X_PHY_BASE(pipe) + 0x694) +#define PE220X_PHY_PLL0_CP_FILT_PADJ(pipe) (PE220X_PHY_BASE(pipe) + 0x698) +#define PE220X_PHY_PLL0_INTDIV(pipe) (PE220X_PHY_BASE(pipe) + 0x240) +#define PE220X_PHY_PLL0_FRACDIVL(pipe) (PE220X_PHY_BASE(pipe) + 0x244) +#define PE220X_PHY_PLL0_FRACDIVH(pipe) (PE220X_PHY_BASE(pipe) + 0x248) +#define PE220X_PHY_PLL0_HIGH_THR(pipe) (PE220X_PHY_BASE(pipe) + 0x24c) +#define PE220X_PHY_PLL0_PDIAG_CTRL(pipe) (PE220X_PHY_BASE(pipe) + 0x680) +#define PE220X_PHY_PLL0_VCOCAL_PLLCNT_START(pipe) (PE220X_PHY_BASE(pipe) + 0x220) +#define PE220X_PHY_PLL0_LOCK_PEFCNT(pipe) (PE220X_PHY_BASE(pipe) + 0x270) +#define PE220X_PHY_PLL0_LOCK_PLLCNT_START(pipe) (PE220X_PHY_BASE(pipe) + 0x278) +#define PE220X_PHY_PLL0_LOCK_PLLCNT_THR(pipe) (PE220X_PHY_BASE(pipe) + 0x27c) + +#define PE220X_PHY_PLL0_TX_PSC_A0(pipe) (PE220X_PHY_BASE(pipe) + 0x18400) +#define PLL0_TX_PSC_A0 0xfb +#define PE220X_PHY_PLL0_TX_PSC_A2(pipe) (PE220X_PHY_BASE(pipe) + 0x18408) +#define PLL0_TX_PSC_A2 0x4aa +#define PE220X_PHY_PLL0_TX_PSC_A3(pipe) (PE220X_PHY_BASE(pipe) + 0x1840c) +#define PLL0_TX_PSC_A3 0x4aa +#define PE220X_PHY_PLL0_RX_PSC_A0(pipe) (PE220X_PHY_BASE(pipe) + 0x28000) +#define PLL0_RX_PSC_A0 0x0 +#define PE220X_PHY_PLL0_RX_PSC_A2(pipe) (PE220X_PHY_BASE(pipe) + 0x28008) +#define PLL0_RX_PSC_A2 0x0 +#define PE220X_PHY_PLL0_RX_PSC_A3(pipe) (PE220X_PHY_BASE(pipe) + 0x2800C) +#define PLL0_RX_PSC_A3 0x0 +#define PE220X_PHY_PLL0_RX_PSC_CAL(pipe) (PE220X_PHY_BASE(pipe) + 0x28018) +#define PLL0_RX_PSC_CAL 0x0 + +#define PE220X_PHY_PLL0_XCVR_CTRL(pipe) (PE220X_PHY_BASE(pipe) + 0x183a8) +#define PLL0_XCVR_CTRL 0xf + +#define PE220X_PHY_PLL0_RX_GCSM1_CTRL(pipe) (PE220X_PHY_BASE(pipe) + 0x28420) +#define PLL0_RX_GCSM1_CTRL 0x0 +#define PE220X_PHY_PLL0_RX_GCSM2_CTRL(pipe) (PE220X_PHY_BASE(pipe) + 0x28440) +#define PLL0_RX_GCSM2_CTRL 0x0 +#define PE220X_PHY_PLL0_RX_PERGCSM_CTRL(pipe) (PE220X_PHY_BASE(pipe) + 0x28460) +#define PLL0_RX_PERGCSM_CTRL 0x0 + +/* swing and emphasis */ +#define PE220X_PHY_PLL0_TX_DIAG_ACYA(pipe) (PE220X_PHY_BASE(pipe) + 0x1879c) +#define LOCK 1 +#define UNLOCK 0 + +#define PE220X_PHY_PLL0_TX_TXCC_CTRL(pipe) (PE220X_PHY_BASE(pipe) + 0x18100) +#define TX_TXCC_CTRL 0x8a4 + +#define PE220X_PHY_PLL0_TX_DRV(pipe) (PE220X_PHY_BASE(pipe) + 0x18318) +#define TX_DRV 0x3 + +#define PE220X_PHY_PLL0_TX_MGNFS(pipe) (PE220X_PHY_BASE(pipe) + 0x18140) + +#define PE220X_PHY_PLL0_TX_CPOST(pipe) (PE220X_PHY_BASE(pipe) + 0x18130) + +#endif /* __PE220X_REG_H__ */ diff --git a/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_crtc.c b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_crtc.c new file mode 100644 index 000000000..f2ed42d6b --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_crtc.c @@ -0,0 +1,829 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Phytium display drm driver + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include "phytium_display_drv.h" +#include "phytium_crtc.h" +#include "phytium_plane.h" +#include "phytium_dp.h" +#include "px210_dc.h" +#include "pe220x_dc.h" +#include "phytium_reg.h" + +#define MAXKERNELSIZE 9 +#define SUBPIXELINDEXBITS 5 +#define SUBPIXELCOUNT (1 << SUBPIXELINDEXBITS) +#define SUBPIXELLOADCOUNT (SUBPIXELCOUNT / 2 + 1) +#define WEIGHTSTATECOUNT (((SUBPIXELLOADCOUNT * MAXKERNELSIZE + 1) & ~1) / 2) +#define KERNELTABLESIZE (SUBPIXELLOADCOUNT * MAXKERNELSIZE * sizeof(uint16_t)) +#define PHYALIGN(n, align) (((n) + ((align) - 1)) & ~((align) - 1)) +#define KERNELSTATES (PHYALIGN(KERNELTABLESIZE + 4, 8)) +#define PHYPI 3.14159265358979323846f + +#define MATH_Add(X, Y) (float)((X) + (Y)) +#define MATH_Multiply(X, Y) (float)((X) * (Y)) +#define MATH_Divide(X, Y) (float)((X) / (Y)) +#define MATH_DivideFromUInteger(X, Y) ((float)(X) / (float)(Y)) +#define MATH_I2Float(X) (float)(X) + +struct filter_blit_array { + uint8_t kernelSize; + uint32_t scaleFactor; + uint32_t *kernelStates; +}; + +static uint32_t dc_scaling_get_factor(uint32_t src_size, uint32_t dst_size) +{ + uint32_t factor = 0; + + factor = ((src_size - 1) << SCALE_FACTOR_SRC_OFFSET) / (dst_size - 1); + + return factor; +} + +static float dc_sint(float x) +{ + const float B = 1.2732395477; + const float C = -0.4052847346; + const float P = 0.2310792853; + float y; + + if (x < 0) + y = B*x - C*x*x; + else + y = B*x + C*x*x; + if (y < 0) + y = P * (y * (0 - y) - y) + y; + else + y = P * (y * y - y) + y; + return y; +} + +static float dc_sinc_filter(float x, int radius) +{ + float pit, pitd, f1, f2, result; + float f_radius = MATH_I2Float(radius); + + if (x == 0.0f) { + result = 1.0f; + } else if ((x < -f_radius) || (x > f_radius)) { + result = 0.0f; + } else { + pit = MATH_Multiply(PHYPI, x); + pitd = MATH_Divide(pit, f_radius); + f1 = MATH_Divide(dc_sint(pit), pit); + f2 = MATH_Divide(dc_sint(pitd), pitd); + result = MATH_Multiply(f1, f2); + } + + return result; +} + +static int dc_calculate_sync_table( + uint8_t kernel_size, + uint32_t src_size, + uint32_t dst_size, + struct filter_blit_array *kernel_info) +{ + uint32_t scale_factor; + float f_scale; + int kernel_half; + float f_subpixel_step; + float f_subpixel_offset; + uint32_t subpixel_pos; + int kernel_pos; + int padding; + uint16_t *kernel_array; + int range = 0; + + do { + /* Compute the scale factor. */ + scale_factor = dc_scaling_get_factor(src_size, dst_size); + + /* Same kernel size and ratio as before? */ + if ((kernel_info->kernelSize == kernel_size) && + (kernel_info->scaleFactor == kernel_size)) { + break; + } + + /* check the array */ + if (kernel_info->kernelStates == NULL) + break; + + /* Store new parameters. */ + kernel_info->kernelSize = kernel_size; + kernel_info->scaleFactor = scale_factor; + + /* Compute the scale factor. */ + f_scale = MATH_DivideFromUInteger(dst_size, src_size); + + /* Adjust the factor for magnification. */ + if (f_scale > 1.0f) + f_scale = 1.0f; + + /* Calculate the kernel half. */ + kernel_half = (int) (kernel_info->kernelSize >> 1); + + /* Calculate the subpixel step. */ + f_subpixel_step = MATH_Divide(1.0f, MATH_I2Float(SUBPIXELCOUNT)); + + /* Init the subpixel offset. */ + f_subpixel_offset = 0.5f; + + /* Determine kernel padding size. */ + padding = (MAXKERNELSIZE - kernel_info->kernelSize) / 2; + + /* Set initial kernel array pointer. */ + kernel_array = (uint16_t *) (kernel_info->kernelStates + 1); + + /* Loop through each subpixel. */ + for (subpixel_pos = 0; subpixel_pos < SUBPIXELLOADCOUNT; subpixel_pos++) { + /* Define a temporary set of weights. */ + float fSubpixelSet[MAXKERNELSIZE]; + + /* Init the sum of all weights for the current subpixel. */ + float fWeightSum = 0.0f; + uint16_t weightSum = 0; + short int adjustCount, adjustFrom; + short int adjustment; + + /* Compute weights. */ + for (kernel_pos = 0; kernel_pos < MAXKERNELSIZE; kernel_pos++) { + /* Determine the current index. */ + int index = kernel_pos - padding; + + /* Pad with zeros. */ + if ((index < 0) || (index >= kernel_info->kernelSize)) { + fSubpixelSet[kernel_pos] = 0.0f; + } else { + if (kernel_info->kernelSize == 1) { + fSubpixelSet[kernel_pos] = 1.0f; + } else { + /* Compute the x position for filter function. */ + float fX = MATH_Add( + MATH_I2Float(index - kernel_half), + f_subpixel_offset); + fX = MATH_Multiply(fX, f_scale); + + /* Compute the weight. */ + fSubpixelSet[kernel_pos] = dc_sinc_filter(fX, + kernel_half); + } + + /* Update the sum of weights. */ + fWeightSum = MATH_Add(fWeightSum, + fSubpixelSet[kernel_pos]); + } + } + + /* Adjust weights so that the sum will be 1.0. */ + for (kernel_pos = 0; kernel_pos < MAXKERNELSIZE; kernel_pos++) { + /* Normalize the current weight. */ + float fWeight = MATH_Divide(fSubpixelSet[kernel_pos], + fWeightSum); + + /* Convert the weight to fixed point and store in the table. */ + if (fWeight == 0.0f) + kernel_array[kernel_pos] = 0x0000; + else if (fWeight >= 1.0f) + kernel_array[kernel_pos] = 0x4000; + else if (fWeight <= -1.0f) + kernel_array[kernel_pos] = 0xC000; + else + kernel_array[kernel_pos] = + (int16_t) MATH_Multiply(fWeight, 16384.0f); + weightSum += kernel_array[kernel_pos]; + } + + /* Adjust the fixed point coefficients. */ + adjustCount = 0x4000 - weightSum; + if (adjustCount < 0) { + adjustCount = -adjustCount; + adjustment = -1; + } else { + adjustment = 1; + } + + adjustFrom = (MAXKERNELSIZE - adjustCount) / 2; + for (kernel_pos = 0; kernel_pos < adjustCount; kernel_pos++) { + range = (MAXKERNELSIZE*subpixel_pos + adjustFrom + kernel_pos) * + sizeof(uint16_t); + if ((range >= 0) && (range < KERNELTABLESIZE)) + kernel_array[adjustFrom + kernel_pos] += adjustment; + else + DRM_ERROR("%s failed\n", __func__); + } + + kernel_array += MAXKERNELSIZE; + + /* Advance to the next subpixel. */ + f_subpixel_offset = MATH_Add(f_subpixel_offset, -f_subpixel_step); + } + } while (0); + + return 0; +} + +static void phytium_dc_scaling_config(struct drm_crtc *crtc, + struct drm_crtc_state *old_state) +{ + struct drm_device *dev = crtc->dev; + struct phytium_display_private *priv = dev->dev_private; + struct drm_display_mode *mode = &crtc->state->adjusted_mode; + struct phytium_crtc *phytium_crtc = to_phytium_crtc(crtc); + int phys_pipe = phytium_crtc->phys_pipe; + uint32_t group_offset = priv->dc_reg_base[phys_pipe]; + uint32_t scale_factor_x, scale_factor_y, i; + uint32_t kernelStates[128]; + struct filter_blit_array kernel_info_width; + void *tmp = NULL; + + if (mode->hdisplay != mode->crtc_hdisplay || mode->vdisplay != mode->crtc_vdisplay) { + phytium_crtc->src_width = mode->hdisplay; + phytium_crtc->src_height = mode->vdisplay; + phytium_crtc->dst_width = mode->crtc_hdisplay; + phytium_crtc->dst_height = mode->crtc_vdisplay; + + phytium_crtc->dst_x = (mode->crtc_hdisplay - phytium_crtc->dst_width) / 2; + phytium_crtc->dst_y = (mode->crtc_vdisplay - phytium_crtc->dst_height) / 2; + + scale_factor_x = dc_scaling_get_factor(phytium_crtc->src_width, + phytium_crtc->dst_width); + scale_factor_y = dc_scaling_get_factor(phytium_crtc->src_height, + phytium_crtc->dst_height); + if (scale_factor_y > (SCALE_FACTOR_Y_MAX << SCALE_FACTOR_SRC_OFFSET)) + scale_factor_y = (SCALE_FACTOR_Y_MAX << SCALE_FACTOR_SRC_OFFSET); + + phytium_writel_reg(priv, scale_factor_x & SCALE_FACTOR_X_MASK, + group_offset, PHYTIUM_DC_FRAMEBUFFER_SCALE_FACTOR_X); + phytium_writel_reg(priv, scale_factor_y & SCALE_FACTOR_Y_MASK, + group_offset, PHYTIUM_DC_FRAMEBUFFER_SCALE_FACTOR_Y); + phytium_writel_reg(priv, FRAMEBUFFER_TAP, + group_offset, PHYTIUM_DC_FRAMEBUFFER_SCALECONFIG); + + tmp = kmalloc(KERNELSTATES, GFP_KERNEL); + if (!tmp) { + DRM_ERROR("malloc %ld failed\n", KERNELSTATES); + return; + } + + memset(&kernel_info_width, 0, sizeof(struct filter_blit_array)); + kernel_info_width.kernelStates = tmp; + memset(kernel_info_width.kernelStates, 0, KERNELSTATES); + kernel_neon_begin(); + dc_calculate_sync_table(FRAMEBUFFER_HORIZONTAL_FILTER_TAP, + phytium_crtc->src_width, + phytium_crtc->dst_width, + &kernel_info_width); + memset(kernelStates, 0, sizeof(kernelStates)); + memcpy(kernelStates, kernel_info_width.kernelStates + 1, KERNELSTATES - 4); + kernel_neon_end(); + phytium_writel_reg(priv, HORI_FILTER_INDEX, + group_offset, PHYTIUM_DC_FRAMEBUFFER_HORI_FILTER_INDEX); + for (i = 0; i < 128; i++) { + phytium_writel_reg(priv, kernelStates[i], + group_offset, PHYTIUM_DC_FRAMEBUFFER_HORI_FILTER); + } + + memset(&kernel_info_width, 0, sizeof(struct filter_blit_array)); + kernel_info_width.kernelStates = tmp; + memset(kernel_info_width.kernelStates, 0, KERNELSTATES); + kernel_neon_begin(); + dc_calculate_sync_table(FRAMEBUFFER_FILTER_TAP, phytium_crtc->src_height, + phytium_crtc->dst_height, &kernel_info_width); + memset(kernelStates, 0, sizeof(kernelStates)); + memcpy(kernelStates, kernel_info_width.kernelStates + 1, KERNELSTATES - 4); + kernel_neon_end(); + phytium_writel_reg(priv, VERT_FILTER_INDEX, + group_offset, PHYTIUM_DC_FRAMEBUFFER_VERT_FILTER_INDEX); + for (i = 0; i < 128; i++) + phytium_writel_reg(priv, kernelStates[i], + group_offset, PHYTIUM_DC_FRAMEBUFFER_VERT_FILTER); + phytium_writel_reg(priv, INITIALOFFSET, + group_offset, PHYTIUM_DC_FRAMEBUFFER_INITIALOFFSET); + kfree(tmp); + phytium_crtc->scale_enable = true; + } else { + phytium_crtc->scale_enable = false; + } +} + +static void phytium_crtc_gamma_set(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct phytium_display_private *priv = dev->dev_private; + struct phytium_crtc *phytium_crtc = to_phytium_crtc(crtc); + int phys_pipe = phytium_crtc->phys_pipe; + uint32_t group_offset = priv->dc_reg_base[phys_pipe]; + uint32_t config = 0, data; + struct drm_crtc_state *state = crtc->state; + struct drm_color_lut *lut; + unsigned long flags; + uint32_t active_line = 0, timeout = 500; + int i; + + if (state->gamma_lut) { + if (WARN((state->gamma_lut->length/sizeof(struct drm_color_lut) != GAMMA_INDEX_MAX), + "gamma size is not match\n")) + return; + lut = (struct drm_color_lut *)state->gamma_lut->data; + + config = phytium_readl_reg(priv, group_offset, PHYTIUM_DC_FRAMEBUFFER_CONFIG); + if (config & FRAMEBUFFER_OUTPUT) { + struct drm_display_mode *mode = &state->adjusted_mode; + uint32_t frame_time; + uint32_t value_a, value_b; + + frame_time = mode->crtc_vtotal * mode->crtc_htotal / mode->crtc_clock; + value_b = (frame_time - 2) * mode->crtc_vtotal; + local_irq_save(flags); + do { + active_line = phytium_readl_reg(priv, group_offset, + PHYTIUM_DC_LOCATION); + active_line = active_line >> LOVATION_Y_SHIFT; + value_a = (mode->crtc_vblank_end - mode->crtc_vblank_start + + active_line) * frame_time; + if (value_a < value_b) + break; + local_irq_restore(flags); + udelay(1000); + timeout--; + local_irq_save(flags); + } while (timeout); + + if (timeout == 0) + DRM_ERROR("wait gamma active line timeout\n"); + } + + phytium_writel_reg(priv, 0, group_offset, PHYTIUM_DC_GAMMA_INDEX); + for (i = 0; i < GAMMA_INDEX_MAX; i++) { + data = ((lut[i].red >> 6) & GAMMA_RED_MASK) << GAMMA_RED_SHIFT; + data |= (((lut[i].green >> 6) & GAMMA_GREEN_MASK) << GAMMA_GREEN_SHIFT); + data |= (((lut[i].blue >> 6) & GAMMA_BLUE_MASK) << GAMMA_BLUE_SHIFT); + phytium_writel_reg(priv, data, group_offset, PHYTIUM_DC_GAMMA_DATA); + } + + if (config & FRAMEBUFFER_OUTPUT) + local_irq_restore(flags); + } +} + +static void phytium_crtc_gamma_init(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct phytium_display_private *priv = dev->dev_private; + struct phytium_crtc *phytium_crtc = to_phytium_crtc(crtc); + int phys_pipe = phytium_crtc->phys_pipe; + uint32_t group_offset = priv->dc_reg_base[phys_pipe]; + struct drm_crtc_state *state = crtc->state; + uint32_t config = 0, data; + uint16_t *red, *green, *blue; + unsigned long flags; + uint32_t active_line = 0, timeout = 500; + int i; + + if (WARN((crtc->gamma_size != GAMMA_INDEX_MAX), "gamma size is not match\n")) + return; + + red = crtc->gamma_store; + green = red + crtc->gamma_size; + blue = green + crtc->gamma_size; + + config = phytium_readl_reg(priv, group_offset, PHYTIUM_DC_FRAMEBUFFER_CONFIG); + if (config & FRAMEBUFFER_OUTPUT) { + struct drm_display_mode *mode = &state->adjusted_mode; + uint32_t frame_time; + uint32_t value_a, value_b; + + frame_time = mode->crtc_vtotal * mode->crtc_htotal / mode->crtc_clock; + value_b = (frame_time - 2) * mode->crtc_vtotal; + local_irq_save(flags); + do { + active_line = phytium_readl_reg(priv, group_offset, PHYTIUM_DC_LOCATION); + active_line = active_line >> LOVATION_Y_SHIFT; + value_a = (mode->crtc_vblank_end - mode->crtc_vblank_start + + active_line) * frame_time; + if (value_a < value_b) + break; + local_irq_restore(flags); + udelay(1000); + timeout--; + local_irq_save(flags); + } while (timeout); + + if (timeout == 0) + DRM_ERROR("wait gamma active line timeout\n"); + } + + phytium_writel_reg(priv, 0, group_offset, PHYTIUM_DC_GAMMA_INDEX); + for (i = 0; i < GAMMA_INDEX_MAX; i++) { + data = ((*red++ >> 6) & GAMMA_RED_MASK) << GAMMA_RED_SHIFT; + data |= (((*green++ >> 6) & GAMMA_GREEN_MASK) << GAMMA_GREEN_SHIFT); + data |= (((*blue++ >> 6) & GAMMA_BLUE_MASK) << GAMMA_BLUE_SHIFT); + phytium_writel_reg(priv, data, group_offset, PHYTIUM_DC_GAMMA_DATA); + } + + if (config & FRAMEBUFFER_OUTPUT) + local_irq_restore(flags); +} + +static void phytium_crtc_destroy(struct drm_crtc *crtc) +{ + struct phytium_crtc *phytium_crtc = to_phytium_crtc(crtc); + + drm_crtc_cleanup(crtc); + kfree(phytium_crtc); +} + +struct drm_crtc_state * +phytium_crtc_atomic_duplicate_state(struct drm_crtc *crtc) +{ + struct phytium_crtc_state *phytium_crtc_state = NULL; + + phytium_crtc_state = kmemdup(crtc->state, sizeof(*phytium_crtc_state), + GFP_KERNEL); + if (!phytium_crtc_state) + return NULL; + __drm_atomic_helper_crtc_duplicate_state(crtc, + &phytium_crtc_state->base); + + return &phytium_crtc_state->base; +} + +void +phytium_crtc_atomic_destroy_state(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + struct phytium_crtc_state *phytium_crtc_state = + to_phytium_crtc_state(state); + + phytium_crtc_state = to_phytium_crtc_state(state); + __drm_atomic_helper_crtc_destroy_state(state); + kfree(phytium_crtc_state); +} + +static int phytium_enable_vblank(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct phytium_display_private *priv = dev->dev_private; + struct phytium_crtc *phytium_crtc = to_phytium_crtc(crtc); + int phys_pipe = phytium_crtc->phys_pipe; + + phytium_writel_reg(priv, INT_ENABLE, priv->dc_reg_base[phys_pipe], PHYTIUM_DC_INT_ENABLE); + + return 0; +} + +static void phytium_disable_vblank(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct phytium_display_private *priv = dev->dev_private; + struct phytium_crtc *phytium_crtc = to_phytium_crtc(crtc); + int phys_pipe = phytium_crtc->phys_pipe; + + phytium_writel_reg(priv, INT_DISABLE, priv->dc_reg_base[phys_pipe], + PHYTIUM_DC_INT_ENABLE); +} + +static const struct drm_crtc_funcs phytium_crtc_funcs = { + .gamma_set = drm_atomic_helper_legacy_gamma_set, + .set_config = drm_atomic_helper_set_config, + .destroy = phytium_crtc_destroy, + .page_flip = drm_atomic_helper_page_flip, + .reset = drm_atomic_helper_crtc_reset, + .atomic_duplicate_state = phytium_crtc_atomic_duplicate_state, + .atomic_destroy_state = phytium_crtc_atomic_destroy_state, + .enable_vblank = phytium_enable_vblank, + .disable_vblank = phytium_disable_vblank, +}; + +static void +phytium_crtc_atomic_enable(struct drm_crtc *crtc, + struct drm_crtc_state *old_state) +{ + struct drm_atomic_state *state = old_state->state; + struct drm_device *dev = crtc->dev; + struct phytium_display_private *priv = dev->dev_private; + struct drm_display_mode *mode = &crtc->state->adjusted_mode; + struct drm_connector_state *new_conn_state; + struct drm_connector *conn; + struct phytium_crtc *phytium_crtc = to_phytium_crtc(crtc); + int phys_pipe = phytium_crtc->phys_pipe; + uint32_t group_offset = priv->dc_reg_base[phys_pipe]; + int config = 0, i = 0; + + for_each_new_connector_in_state(state, conn, new_conn_state, i) { + if (new_conn_state->crtc != crtc) + continue; + + switch (conn->display_info.bpc) { + case 10: + phytium_crtc->bpc = DP_RGB101010; + break; + case 6: + phytium_crtc->bpc = DP_RGB666; + break; + default: + phytium_crtc->bpc = DP_RGB888; + break; + } + } + + /* config pix clock */ + phytium_crtc->dc_hw_config_pix_clock(crtc, mode->clock); + + phytium_dc_scaling_config(crtc, old_state); + config = ((mode->crtc_hdisplay & HDISPLAY_END_MASK) << HDISPLAY_END_SHIFT) + | ((mode->crtc_htotal&HDISPLAY_TOTAL_MASK) << HDISPLAY_TOTAL_SHIFT); + phytium_writel_reg(priv, config, group_offset, PHYTIUM_DC_HDISPLAY); + config = ((mode->crtc_hsync_start & HSYNC_START_MASK) << HSYNC_START_SHIFT) + | ((mode->crtc_hsync_end & HSYNC_END_MASK) << HSYNC_END_SHIFT) + | HSYNC_PULSE_ENABLED; + config |= (mode->flags & DRM_MODE_FLAG_PHSYNC) ? 0 : HSYNC_NEGATIVE; + phytium_writel_reg(priv, config, group_offset, PHYTIUM_DC_HSYNC); + config = ((mode->crtc_vdisplay & VDISPLAY_END_MASK) << VDISPLAY_END_SHIFT) + | ((mode->crtc_vtotal & VDISPLAY_TOTAL_MASK) << VDISPLAY_TOTAL_SHIFT); + phytium_writel_reg(priv, config, group_offset, PHYTIUM_DC_VDISPLAY); + config = ((mode->crtc_vsync_start & VSYNC_START_MASK) << VSYNC_START_SHIFT) + | ((mode->crtc_vsync_end & VSYNC_END_MASK) << VSYNC_END_SHIFT) + | VSYNC_PULSE_ENABLED; + config |= (mode->flags & DRM_MODE_FLAG_PVSYNC) ? 0 : VSYNC_NEGATIVE; + phytium_writel_reg(priv, config, group_offset, PHYTIUM_DC_VSYNC); + config = PANEL_DATAENABLE_ENABLE | PANEL_DATA_ENABLE | PANEL_CLOCK_ENABLE; + phytium_writel_reg(priv, config, group_offset, PHYTIUM_DC_PANEL_CONFIG); + config = phytium_crtc->bpc | OUTPUT_DP; + phytium_writel_reg(priv, config, group_offset, PHYTIUM_DC_DP_CONFIG); + + config = phytium_readl_reg(priv, group_offset, PHYTIUM_DC_FRAMEBUFFER_CONFIG); + + if (crtc->state->active) + config |= FRAMEBUFFER_OUTPUT | FRAMEBUFFER_RESET; + else + config &= (~(FRAMEBUFFER_OUTPUT | FRAMEBUFFER_RESET)); + + if (phytium_crtc->scale_enable) + config |= FRAMEBUFFER_SCALE_ENABLE; + else + config &= (~FRAMEBUFFER_SCALE_ENABLE); + + config |= FRAMEBUFFER_GAMMA_ENABLE; + + if (crtc->state->gamma_lut) + phytium_crtc_gamma_set(crtc); + else + phytium_crtc_gamma_init(crtc); + + phytium_writel_reg(priv, config, group_offset, PHYTIUM_DC_FRAMEBUFFER_CONFIG); + drm_crtc_vblank_on(crtc); +} + +static void +phytium_crtc_atomic_disable(struct drm_crtc *crtc, + struct drm_crtc_state *old_state) +{ + struct phytium_crtc *phytium_crtc = to_phytium_crtc(crtc); + + drm_crtc_vblank_off(crtc); + phytium_crtc->dc_hw_disable(crtc); +} + +static void phytium_crtc_update_timing_for_drm_display_mode(struct drm_display_mode *drm_mode, + const struct drm_display_mode *native_mode) +{ + if (native_mode->clock == drm_mode->clock && + native_mode->htotal == drm_mode->htotal && + native_mode->vtotal == drm_mode->vtotal) { + drm_mode->crtc_hdisplay = native_mode->crtc_hdisplay; + drm_mode->crtc_vdisplay = native_mode->crtc_vdisplay; + drm_mode->crtc_clock = native_mode->crtc_clock; + drm_mode->crtc_hblank_start = native_mode->crtc_hblank_start; + drm_mode->crtc_hblank_end = native_mode->crtc_hblank_end; + drm_mode->crtc_hsync_start = native_mode->crtc_hsync_start; + drm_mode->crtc_hsync_end = native_mode->crtc_hsync_end; + drm_mode->crtc_htotal = native_mode->crtc_htotal; + drm_mode->crtc_hskew = native_mode->crtc_hskew; + drm_mode->crtc_vblank_start = native_mode->crtc_vblank_start; + drm_mode->crtc_vblank_end = native_mode->crtc_vblank_end; + drm_mode->crtc_vsync_start = native_mode->crtc_vsync_start; + drm_mode->crtc_vsync_end = native_mode->crtc_vsync_end; + drm_mode->crtc_vtotal = native_mode->crtc_vtotal; + } +} + +static int +phytium_crtc_atomic_check(struct drm_crtc *crtc, struct drm_crtc_state *crtc_state) +{ + struct drm_atomic_state *state = crtc_state->state; + struct drm_plane_state *new_plane_state = NULL; + int ret = 0; + struct drm_connector *connector; + struct drm_connector_state *new_con_state; + uint32_t i; + struct phytium_dp_device *phytium_dp = NULL; + + for_each_new_connector_in_state(state, connector, new_con_state, i) { + if (new_con_state->crtc == crtc) { + phytium_dp = connector_to_dp_device(connector); + break; + } + } + if (phytium_dp) + phytium_crtc_update_timing_for_drm_display_mode(&crtc_state->adjusted_mode, + &phytium_dp->native_mode); + + new_plane_state = drm_atomic_get_new_plane_state(crtc_state->state, + crtc->primary); + if (crtc_state->enable && new_plane_state && !new_plane_state->crtc) { + ret = -EINVAL; + goto fail; + } + + return 0; +fail: + return ret; +} + +static void +phytium_crtc_atomic_begin(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) +{ + struct drm_device *dev = crtc->dev; + struct phytium_display_private *priv = dev->dev_private; + struct phytium_crtc *phytium_crtc = to_phytium_crtc(crtc); + int phys_pipe = phytium_crtc->phys_pipe, config; + uint32_t group_offset = priv->dc_reg_base[phys_pipe]; + + config = phytium_readl_reg(priv, group_offset, PHYTIUM_DC_FRAMEBUFFER_CONFIG); + if (config & FRAMEBUFFER_RESET) { + phytium_writel_reg(priv, config | FRAMEBUFFER_VALID_PENDING, + group_offset, PHYTIUM_DC_FRAMEBUFFER_CONFIG); + } +} + +static void phytium_crtc_atomic_flush(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) +{ + struct drm_device *dev = crtc->dev; + struct phytium_display_private *priv = dev->dev_private; + struct phytium_crtc *phytium_crtc = to_phytium_crtc(crtc); + struct phytium_crtc_state *phytium_crtc_state = NULL; + int phys_pipe = phytium_crtc->phys_pipe, config; + uint32_t group_offset = priv->dc_reg_base[phys_pipe]; + + DRM_DEBUG_KMS("crtc->state active:%d enable:%d\n", + crtc->state->active, crtc->state->enable); + phytium_crtc_state = to_phytium_crtc_state(crtc->state); + + if (crtc->state->color_mgmt_changed) + phytium_crtc_gamma_set(crtc); + + config = phytium_readl_reg(priv, group_offset, PHYTIUM_DC_FRAMEBUFFER_CONFIG); + phytium_writel_reg(priv, config&(~FRAMEBUFFER_VALID_PENDING), + group_offset, PHYTIUM_DC_FRAMEBUFFER_CONFIG); + + if (crtc->state->event) { + DRM_DEBUG_KMS("vblank->refcount:%d\n", + atomic_read(&dev->vblank[0].refcount)); + spin_lock_irq(&dev->event_lock); + if (drm_crtc_vblank_get(crtc) == 0) + drm_crtc_arm_vblank_event(crtc, crtc->state->event); + else + drm_crtc_send_vblank_event(crtc, crtc->state->event); + crtc->state->event = NULL; + spin_unlock_irq(&dev->event_lock); + } +} + +static enum drm_mode_status +phytium_crtc_mode_valid(struct drm_crtc *crtc, const struct drm_display_mode *mode) +{ + struct drm_device *dev = crtc->dev; + struct phytium_display_private *priv = dev->dev_private; + + if (mode->crtc_clock > priv->info.crtc_clock_max) + return MODE_CLOCK_HIGH; + + if (mode->hdisplay > priv->info.hdisplay_max) + return MODE_BAD_HVALUE; + + if (mode->vdisplay > priv->info.vdisplay_max) + return MODE_BAD_VVALUE; + + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + return MODE_NO_INTERLACE; + + return MODE_OK; +} + +static const struct drm_crtc_helper_funcs phytium_crtc_helper_funcs = { + .mode_valid = phytium_crtc_mode_valid, + .atomic_check = phytium_crtc_atomic_check, + .atomic_begin = phytium_crtc_atomic_begin, + .atomic_flush = phytium_crtc_atomic_flush, + .atomic_enable = phytium_crtc_atomic_enable, + .atomic_disable = phytium_crtc_atomic_disable, +}; + +void phytium_crtc_resume(struct drm_device *drm_dev) +{ + struct drm_crtc *crtc; + struct phytium_crtc *phytium_crtc = NULL; + + drm_for_each_crtc(crtc, drm_dev) { + phytium_crtc = to_phytium_crtc(crtc); + if (phytium_crtc->dc_hw_reset) + phytium_crtc->dc_hw_reset(crtc); + phytium_crtc_gamma_init(crtc); + } +} + +int phytium_crtc_init(struct drm_device *dev, int phys_pipe) +{ + struct phytium_crtc *phytium_crtc; + struct phytium_crtc_state *phytium_crtc_state; + struct phytium_plane *phytium_primary_plane = NULL; + struct phytium_plane *phytium_cursor_plane = NULL; + struct phytium_display_private *priv = dev->dev_private; + int ret; + + phytium_crtc = kzalloc(sizeof(*phytium_crtc), GFP_KERNEL); + if (!phytium_crtc) { + ret = -ENOMEM; + goto failed_malloc_crtc; + } + + phytium_crtc_state = kzalloc(sizeof(*phytium_crtc_state), GFP_KERNEL); + if (!phytium_crtc_state) { + ret = -ENOMEM; + goto failed_malloc_crtc_state; + } + + phytium_crtc_state->base.crtc = &phytium_crtc->base; + phytium_crtc->base.state = &phytium_crtc_state->base; + phytium_crtc->phys_pipe = phys_pipe; + + if (IS_PX210(priv)) { + phytium_crtc->dc_hw_config_pix_clock = px210_dc_hw_config_pix_clock; + phytium_crtc->dc_hw_disable = px210_dc_hw_disable; + phytium_crtc->dc_hw_reset = NULL; + priv->dc_reg_base[phys_pipe] = PX210_DC_BASE(phys_pipe); + priv->dcreq_reg_base[phys_pipe] = PX210_DCREQ_BASE(phys_pipe); + priv->address_transform_base = PX210_ADDRESS_TRANSFORM_BASE; + } else if (IS_PE220X(priv)) { + phytium_crtc->dc_hw_config_pix_clock = pe220x_dc_hw_config_pix_clock; + phytium_crtc->dc_hw_disable = pe220x_dc_hw_disable; + phytium_crtc->dc_hw_reset = pe220x_dc_hw_reset; + priv->dc_reg_base[phys_pipe] = PE220X_DC_BASE(phys_pipe); + priv->dcreq_reg_base[phys_pipe] = 0x0; + priv->address_transform_base = PE220X_ADDRESS_TRANSFORM_BASE; + } + + phytium_primary_plane = phytium_primary_plane_create(dev, phys_pipe); + if (IS_ERR(phytium_primary_plane)) { + ret = PTR_ERR(phytium_primary_plane); + DRM_ERROR("create primary plane failed, phys_pipe(%d)\n", phys_pipe); + goto failed_create_primary; + } + + phytium_cursor_plane = phytium_cursor_plane_create(dev, phys_pipe); + if (IS_ERR(phytium_cursor_plane)) { + ret = PTR_ERR(phytium_cursor_plane); + DRM_ERROR("create cursor plane failed, phys_pipe(%d)\n", phys_pipe); + goto failed_create_cursor; + } + + ret = drm_crtc_init_with_planes(dev, &phytium_crtc->base, + &phytium_primary_plane->base, + &phytium_cursor_plane->base, + &phytium_crtc_funcs, + "phys_pipe %d", phys_pipe); + + if (ret) { + DRM_ERROR("init crtc with plane failed, phys_pipe(%d)\n", phys_pipe); + goto failed_crtc_init; + } + drm_crtc_helper_add(&phytium_crtc->base, &phytium_crtc_helper_funcs); + drm_crtc_vblank_reset(&phytium_crtc->base); + drm_mode_crtc_set_gamma_size(&phytium_crtc->base, GAMMA_INDEX_MAX); + drm_crtc_enable_color_mgmt(&phytium_crtc->base, 0, false, GAMMA_INDEX_MAX); + if (phytium_crtc->dc_hw_reset) + phytium_crtc->dc_hw_reset(&phytium_crtc->base); + + return 0; + +failed_crtc_init: +failed_create_cursor: + /* drm_mode_config_cleanup() will free any crtcs/planes already initialized */ +failed_create_primary: + kfree(phytium_crtc_state); +failed_malloc_crtc_state: + kfree(phytium_crtc); +failed_malloc_crtc: + return ret; +} diff --git a/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_crtc.h b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_crtc.h new file mode 100644 index 000000000..86f894ba5 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_crtc.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Phytium display drm driver + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#ifndef __PHYTIUM_CRTC_H__ +#define __PHYTIUM_CRTC_H__ + +struct phytium_crtc { + struct drm_crtc base; + int phys_pipe; + unsigned int bpc; + + /* scale */ + uint32_t src_width; + uint32_t src_height; + uint32_t dst_width; + uint32_t dst_height; + uint32_t dst_x; + uint32_t dst_y; + bool scale_enable; + bool reserve[3]; + + void (*dc_hw_config_pix_clock)(struct drm_crtc *crtc, int clock); + void (*dc_hw_disable)(struct drm_crtc *crtc); + void (*dc_hw_reset)(struct drm_crtc *crtc); +}; + +struct phytium_crtc_state { + struct drm_crtc_state base; +}; + +#define to_phytium_crtc(x) container_of(x, struct phytium_crtc, base) +#define to_phytium_crtc_state(x) container_of(x, struct phytium_crtc_state, base) + +void phytium_crtc_resume(struct drm_device *drm_dev); +int phytium_crtc_init(struct drm_device *dev, int pipe); +#endif /* __PHYTIUM_CRTC_H__ */ diff --git a/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_debugfs.c b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_debugfs.c new file mode 100644 index 000000000..13657a768 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_debugfs.c @@ -0,0 +1,456 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Phytium display drm driver + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include "phytium_display_drv.h" +#include "phytium_dp.h" +#include "phytium_reg.h" + +const char *const mem_state[PHYTIUM_MEM_STATE_TYPE_COUNT] = { + "Memory_Vram_Total", + "Memory_Vram_Alloc", + "Memory_System_Carveout_Total", + "Memory_System_Carveout_Alloc", + "Memory_System_Alloc", +}; + +static ssize_t +phytium_dp_register_write(struct file *filp, + const char __user *ubuf, + size_t len, + loff_t *ppos) +{ + char tmp[16]; + + if (len >= sizeof(tmp)) + return -EINVAL; + + memset(tmp, 0, sizeof(tmp)); + if (copy_from_user(tmp, ubuf, len)) + return -EFAULT; + tmp[len] = '\0'; + + return len; +} + +static int phytium_dp_register_show(struct seq_file *m, void *data) +{ + struct drm_connector *connector = m->private; + struct phytium_dp_device *phytium_dp = connector_to_dp_device(connector); + struct drm_device *dev = phytium_dp->dev; + struct phytium_display_private *priv = dev->dev_private; + int port = phytium_dp->port; + uint32_t group_offset = priv->dp_reg_base[port]; + + seq_printf(m, "addr:h0x%08x h0x%08x\n", PHYTIUM_DP_M_VID, + phytium_readl_reg(priv, group_offset, PHYTIUM_DP_M_VID)); + seq_printf(m, "addr:h0x%08x h0x%08x\n", PHYTIUM_DP_N_VID, + phytium_readl_reg(priv, group_offset, PHYTIUM_DP_N_VID)); + seq_printf(m, "addr:h0x%08x h0x%08x\n", PHYTIUM_DP_TRANSFER_UNIT_SIZE, + phytium_readl_reg(priv, group_offset, PHYTIUM_DP_TRANSFER_UNIT_SIZE)); + seq_printf(m, "addr:h0x%08x h0x%08x\n", PHYTIUM_DP_DATA_COUNT, + phytium_readl_reg(priv, group_offset, PHYTIUM_DP_DATA_COUNT)); + seq_printf(m, "addr:h0x%08x h0x%08x\n", PHYTIUM_DP_MAIN_LINK_HTOTAL, + phytium_readl_reg(priv, group_offset, PHYTIUM_DP_MAIN_LINK_HTOTAL)); + seq_printf(m, "addr:h0x%08x h0x%08x\n", PHYTIUM_DP_MAIN_LINK_HRES, + phytium_readl_reg(priv, group_offset, PHYTIUM_DP_MAIN_LINK_HRES)); + seq_printf(m, "addr:h0x%08x h0x%08x\n", PHYTIUM_DP_MAIN_LINK_HSWIDTH, + phytium_readl_reg(priv, group_offset, PHYTIUM_DP_MAIN_LINK_HSWIDTH)); + seq_printf(m, "addr:h0x%08x h0x%08x\n", PHYTIUM_DP_MAIN_LINK_HSTART, + phytium_readl_reg(priv, group_offset, PHYTIUM_DP_MAIN_LINK_HSTART)); + seq_printf(m, "addr:h'0x%08x h'0x%08x\n", PHYTIUM_DP_MAIN_LINK_VTOTAL, + phytium_readl_reg(priv, group_offset, PHYTIUM_DP_MAIN_LINK_VTOTAL)); + seq_printf(m, "addr:h'0x%08x h'0x%08x\n", PHYTIUM_DP_MAIN_LINK_VRES, + phytium_readl_reg(priv, group_offset, PHYTIUM_DP_MAIN_LINK_VRES)); + seq_printf(m, "addr:h'0x%08x h'0x%08x\n", PHYTIUM_DP_MAIN_LINK_VSWIDTH, + phytium_readl_reg(priv, group_offset, PHYTIUM_DP_MAIN_LINK_VSWIDTH)); + seq_printf(m, "addr:h'0x%08x h'0x%08x\n", PHYTIUM_DP_MAIN_LINK_VSTART, + phytium_readl_reg(priv, group_offset, PHYTIUM_DP_MAIN_LINK_VSTART)); + seq_printf(m, "addr:h'0x%08x h'0x%08x\n", PHYTIUM_DP_MAIN_LINK_POLARITY, + phytium_readl_reg(priv, group_offset, PHYTIUM_DP_MAIN_LINK_POLARITY)); + seq_printf(m, "addr:h'0x%08x h'0x%08x\n", PHYTIUM_DP_MAIN_LINK_MISC0, + phytium_readl_reg(priv, group_offset, PHYTIUM_DP_MAIN_LINK_MISC0)); + seq_printf(m, "addr:h'0x%08x h'0x%08x\n", PHYTIUM_DP_MAIN_LINK_MISC1, + phytium_readl_reg(priv, group_offset, PHYTIUM_DP_MAIN_LINK_MISC1)); + seq_printf(m, "addr:h'0x%08x h'0x%08x\n", PHYTIUM_DP_USER_SYNC_POLARITY, + phytium_readl_reg(priv, group_offset, PHYTIUM_DP_USER_SYNC_POLARITY)); + seq_printf(m, "addr:h'0x%08x h'0x%08x\n", PHYTIUM_DP_VIDEO_STREAM_ENABLE, + phytium_readl_reg(priv, group_offset, PHYTIUM_DP_VIDEO_STREAM_ENABLE)); + seq_printf(m, "addr:h'0x%08x h'0x%08x\n", PHYTIUM_DP_SECONDARY_STREAM_ENABLE, + phytium_readl_reg(priv, group_offset, PHYTIUM_DP_SECONDARY_STREAM_ENABLE)); + seq_puts(m, "audio:\n"); + seq_printf(m, "addr:h'0x%08x h'0x%08x\n", PHYTIUM_DP_SEC_INPUT_SELECT, + phytium_readl_reg(priv, group_offset, PHYTIUM_DP_SEC_INPUT_SELECT)); + seq_printf(m, "addr:h'0x%08x h'0x%08x\n", PHYTIUM_DP_SEC_DIRECT_CLKDIV, + phytium_readl_reg(priv, group_offset, PHYTIUM_DP_SEC_DIRECT_CLKDIV)); + seq_printf(m, "addr:h'0x%08x h'0x%08x\n", PHYTIUM_DP_SEC_CHANNEL_COUNT, + phytium_readl_reg(priv, group_offset, PHYTIUM_DP_SEC_CHANNEL_COUNT)); + seq_printf(m, "addr:h'0x%08x h'0x%08x\n", PHYTIUM_DP_SEC_CHANNEL_MAP, + phytium_readl_reg(priv, group_offset, PHYTIUM_DP_SEC_CHANNEL_MAP)); + seq_printf(m, "addr:h'0x%08x h'0x%08x\n", PHYTIUM_DP_SEC_DATA_WINDOW, + phytium_readl_reg(priv, group_offset, PHYTIUM_DP_SEC_DATA_WINDOW)); + seq_printf(m, "addr:h'0x%08x h'0x%08x\n", PHYTIUM_DP_SEC_CS_CATEGORY_CODE, + phytium_readl_reg(priv, group_offset, PHYTIUM_DP_SEC_CS_CATEGORY_CODE)); + seq_printf(m, "addr:h'0x%08x h'0x%08x\n", PHYTIUM_DP_SEC_MAUD, + phytium_readl_reg(priv, group_offset, PHYTIUM_DP_SEC_MAUD)); + seq_printf(m, "addr:h'0x%08x h'0x%08x\n", PHYTIUM_DP_SEC_NAUD, + phytium_readl_reg(priv, group_offset, PHYTIUM_DP_SEC_NAUD)); + seq_printf(m, "addr:h'0x%08x h'0x%08x\n", PHYTIUM_DP_SEC_CLOCK_MODE, + phytium_readl_reg(priv, group_offset, PHYTIUM_DP_SEC_CLOCK_MODE)); + seq_printf(m, "addr:h'0x%08x h'0x%08x\n", PHYTIUM_DP_SEC_CS_SOURCE_FORMAT, + phytium_readl_reg(priv, group_offset, PHYTIUM_DP_SEC_CS_SOURCE_FORMAT)); + seq_printf(m, "addr:h'0x%08x h'0x%08x\n", PHYTIUM_DP_SEC_CS_LENGTH_ORIG_FREQ, + phytium_readl_reg(priv, group_offset, PHYTIUM_DP_SEC_CS_LENGTH_ORIG_FREQ)); + seq_printf(m, "addr:h'0x%08x h'0x%08x\n", PHYTIUM_DP_SEC_CS_FREQ_CLOCK_ACCURACY, + phytium_readl_reg(priv, group_offset, PHYTIUM_DP_SEC_CS_FREQ_CLOCK_ACCURACY)); + seq_printf(m, "addr:h'0x%08x h'0x%08x\n", PHYTIUM_DP_SEC_AUDIO_ENABLE, + phytium_readl_reg(priv, group_offset, PHYTIUM_DP_SEC_AUDIO_ENABLE)); + + return 0; +} + +static int phytium_dp_register_open(struct inode *inode, struct file *file) +{ + return single_open(file, phytium_dp_register_show, inode->i_private); +} + +static const struct file_operations phytium_dp_register_fops = { + .owner = THIS_MODULE, + .open = phytium_dp_register_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = phytium_dp_register_write, +}; + +static ssize_t +phytium_dp_trigger_train_fail_write(struct file *filp, + const char __user *ubuf, + size_t len, + loff_t *ppos) +{ + struct seq_file *m = filp->private_data; + struct drm_connector *connector = m->private; + struct phytium_dp_device *phytium_dp = connector_to_dp_device(connector); + char tmp[16]; + + if (len >= sizeof(tmp)) + return -EINVAL; + + memset(tmp, 0, sizeof(tmp)); + if (copy_from_user(tmp, ubuf, len)) + return -EFAULT; + tmp[len] = '\0'; + + if (kstrtouint(tmp, 10, &phytium_dp->trigger_train_fail) != 0) + return -EINVAL; + + return len; +} + +static int phytium_dp_trigger_train_fail_show(struct seq_file *m, void *data) +{ + struct drm_connector *connector = m->private; + struct phytium_dp_device *phytium_dp = connector_to_dp_device(connector); + + seq_printf(m, "trigger_train_fail: %d\n", phytium_dp->trigger_train_fail); + seq_printf(m, "train_retry_count: %d\n", phytium_dp->train_retry_count); + + return 0; +} + +static int phytium_dp_trigger_train_fail_open(struct inode *inode, struct file *file) +{ + return single_open(file, phytium_dp_trigger_train_fail_show, inode->i_private); +} + +static const struct file_operations phytium_dp_trigger_train_fail_fops = { + .owner = THIS_MODULE, + .open = phytium_dp_trigger_train_fail_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = phytium_dp_trigger_train_fail_write, +}; + +static int phytium_edp_backlight_show(struct seq_file *m, void *data) +{ + struct drm_connector *connector = m->private; + struct phytium_dp_device *phytium_dp = connector_to_dp_device(connector); + + if (!phytium_dp->is_edp) + return -ENODEV; + + mutex_lock(&phytium_dp->panel.panel_lock); + seq_printf(m, "backlight: %s\n", phytium_dp->panel.backlight_enabled?"enabled":"disabled"); + mutex_unlock(&phytium_dp->panel.panel_lock); + + return 0; +} + +static int phytium_edp_backlight_open(struct inode *inode, struct file *file) +{ + return single_open(file, phytium_edp_backlight_show, inode->i_private); +} + +static const struct file_operations phytium_edp_backlight_fops = { + .owner = THIS_MODULE, + .open = phytium_edp_backlight_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int phytium_edp_power_show(struct seq_file *m, void *data) +{ + struct drm_connector *connector = m->private; + struct phytium_dp_device *phytium_dp = connector_to_dp_device(connector); + + if (!phytium_dp->is_edp) + return -ENODEV; + + mutex_lock(&phytium_dp->panel.panel_lock); + seq_printf(m, "power: %s\n", phytium_dp->panel.power_enabled?"enabled":"disabled"); + mutex_unlock(&phytium_dp->panel.panel_lock); + + return 0; +} + +static int phytium_edp_power_open(struct inode *inode, struct file *file) +{ + return single_open(file, phytium_edp_power_show, inode->i_private); +} + +static const struct file_operations phytium_edp_power_fops = { + .owner = THIS_MODULE, + .open = phytium_edp_power_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +struct dpcd_block { + /* DPCD dump start address. */ + unsigned int offset; + /* DPCD dump end address, inclusive. If unset, .size will be used. */ + unsigned int end; + /* DPCD dump size. Used if .end is unset. If unset, defaults to 1. */ + size_t size; + /* Only valid for eDP. */ + bool edp; +}; + +static const struct dpcd_block phytium_dpcd_debug[] = { + { .offset = DP_DPCD_REV, .size = DP_RECEIVER_CAP_SIZE }, + { .offset = DP_PSR_SUPPORT, .end = DP_PSR_CAPS }, + { .offset = DP_DOWNSTREAM_PORT_0, .size = 16 }, + { .offset = DP_LINK_BW_SET, .end = DP_EDP_CONFIGURATION_SET }, + { .offset = DP_SINK_COUNT, .end = DP_ADJUST_REQUEST_LANE2_3 }, + { .offset = DP_SET_POWER }, + { .offset = DP_EDP_DPCD_REV }, + { .offset = DP_EDP_GENERAL_CAP_1, .end = DP_EDP_GENERAL_CAP_3 }, + { .offset = DP_EDP_DISPLAY_CONTROL_REGISTER, .end = DP_EDP_BACKLIGHT_FREQ_CAP_MAX_LSB }, + { .offset = DP_EDP_DBC_MINIMUM_BRIGHTNESS_SET, .end = DP_EDP_DBC_MAXIMUM_BRIGHTNESS_SET }, + { .offset = DP_DEVICE_SERVICE_IRQ_VECTOR, .size = 1 }, + { .offset = DP_TEST_REQUEST, .end = DP_TEST_PATTERN }, +}; + +static int phytium_dpcd_show(struct seq_file *m, void *data) +{ + struct drm_connector *connector = m->private; + struct phytium_dp_device *phytium_dp = connector_to_dp_device(connector); + uint8_t buf[16], i; + ssize_t err; + + if (connector->status != connector_status_connected) + return -ENODEV; + + for (i = 0; i < ARRAY_SIZE(phytium_dpcd_debug); i++) { + const struct dpcd_block *b = &phytium_dpcd_debug[i]; + size_t size = b->end ? b->end - b->offset + 1 : (b->size ?: 1); + + if (WARN_ON(size > sizeof(buf))) + continue; + + err = drm_dp_dpcd_read(&phytium_dp->aux, b->offset, buf, size); + if (err <= 0) { + DRM_ERROR("dpcd read (%zu bytes at %u) failed (%zd)\n", + size, b->offset, err); + continue; + } + + seq_printf(m, "%04x: %*ph\n", b->offset, (int) size, buf); + } + + return 0; +} + +static int phytium_dpcd_open(struct inode *inode, struct file *file) +{ + return single_open(file, phytium_dpcd_show, inode->i_private); +} + +static const struct file_operations phytium_dpcd_fops = { + .owner = THIS_MODULE, + .open = phytium_dpcd_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static ssize_t +phytium_dp_state_write(struct file *filp, + const char __user *ubuf, + size_t len, + loff_t *ppos) +{ + char tmp[16]; + + if (len >= sizeof(tmp)) + return -EINVAL; + + memset(tmp, 0, sizeof(tmp)); + if (copy_from_user(tmp, ubuf, len)) + return -EFAULT; + tmp[len] = '\0'; + + return len; +} + +static int phytium_dp_state_show(struct seq_file *m, void *data) +{ + struct drm_connector *connector = m->private; + struct phytium_dp_device *phytium_dp = connector_to_dp_device(connector); + + seq_printf(m, "port number: %d\n", phytium_dp->port); + seq_printf(m, "source_max_lane_count: %d\n", phytium_dp->source_max_lane_count); + seq_printf(m, "max_source_rates: %d\n", + phytium_dp->source_rates[phytium_dp->num_source_rates-1]); + if (connector->status == connector_status_connected) { + seq_printf(m, "sink_max_lane_count: %d\n", phytium_dp->sink_max_lane_count); + seq_printf(m, "max_sink_rates: %d\n", + phytium_dp->sink_rates[phytium_dp->num_sink_rates-1]); + seq_printf(m, "link_rate: %d\n", phytium_dp->link_rate); + seq_printf(m, "link_lane_count: %d\n", phytium_dp->link_lane_count); + seq_printf(m, "train_set[0]: %d\n", phytium_dp->train_set[0]); + seq_printf(m, "has_audio: %s\n", phytium_dp->has_audio?"yes":"no"); + } + + return 0; +} + +static int phytium_dp_state_open(struct inode *inode, struct file *file) +{ + return single_open(file, phytium_dp_state_show, inode->i_private); +} + +static const struct file_operations phytium_dp_state_fops = { + .owner = THIS_MODULE, + .open = phytium_dp_state_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = phytium_dp_state_write, +}; + +static const struct phytium_debugfs_files { + const char *name; + const struct file_operations *fops; +} phytium_debugfs_connector_files[] = { + {"dp_state", &phytium_dp_state_fops}, + {"dpcd", &phytium_dpcd_fops}, + {"dp_register", &phytium_dp_register_fops}, + {"dp_trigger_train_fail", &phytium_dp_trigger_train_fail_fops}, +}; + +static const struct phytium_debugfs_files phytium_edp_debugfs_connector_files[] = { + {"edp_power", &phytium_edp_power_fops}, + {"edp_backlight", &phytium_edp_backlight_fops}, +}; + +int phytium_debugfs_connector_add(struct drm_connector *connector) +{ + struct dentry *root = connector->debugfs_entry; + struct dentry *ent; + int i; + struct phytium_dp_device *phytium_dp = connector_to_dp_device(connector); + + if (!root) + return -ENODEV; + + for (i = 0; i < ARRAY_SIZE(phytium_debugfs_connector_files); i++) { + ent = debugfs_create_file(phytium_debugfs_connector_files[i].name, + 0644, + root, + connector, + phytium_debugfs_connector_files[i].fops); + if (!ent) + return -ENOMEM; + } + + if (phytium_dp->is_edp) + for (i = 0; i < ARRAY_SIZE(phytium_edp_debugfs_connector_files); i++) { + ent = debugfs_create_file(phytium_edp_debugfs_connector_files[i].name, + 0644, + root, + connector, + phytium_edp_debugfs_connector_files[i].fops); + if (!ent) + return -ENOMEM; + } + + return 0; +} + +static int phytium_mem_state_show(struct seq_file *m, void *data) +{ + struct phytium_display_private *priv = m->private; + uint8_t i; + + for (i = 0; i < ARRAY_SIZE(mem_state); i++) + seq_printf(m, "%-34s %10lld\n", mem_state[i], priv->mem_state[i]); + + return 0; +} + +static int phytium_mem_state_open(struct inode *inode, struct file *file) +{ + return single_open(file, phytium_mem_state_show, inode->i_private); +} + +static const struct file_operations phytium_mem_state_fops = { + .owner = THIS_MODULE, + .open = phytium_mem_state_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static const struct phytium_debugfs_files phytium_debugfs_display_files[] = { + {"mem_state", &phytium_mem_state_fops}, +}; + +int phytium_debugfs_display_register(struct phytium_display_private *priv) +{ + struct drm_minor *minor = priv->dev->primary; + struct dentry *root = minor->debugfs_root; + struct dentry *ent; + + if (!root) + return -ENODEV; + + ent = debugfs_create_file(phytium_debugfs_display_files[0].name, + 0644, + root, + priv, + phytium_debugfs_display_files[0].fops); + if (!ent) + return -ENOMEM; + + return 0; +} diff --git a/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_debugfs.h b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_debugfs.h new file mode 100644 index 000000000..aa8e2922e --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_debugfs.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Phytium display drm driver + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#ifndef __PHYTIUM_DEBUGFS_H__ +#define __PHYTIUM_DEBUGFS_H__ + +int phytium_debugfs_connector_add(struct drm_connector *connector); +int phytium_debugfs_display_register(struct phytium_display_private *priv); + +#endif /* __PHYTIUM_DEBUGFS_H__ */ diff --git a/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_display_drv.c b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_display_drv.c new file mode 100644 index 000000000..d3c654b42 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_display_drv.c @@ -0,0 +1,439 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Phytium display drm driver + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "phytium_display_drv.h" +#include "phytium_plane.h" +#include "phytium_crtc.h" +#include "phytium_dp.h" +#include "phytium_gem.h" +#include "phytium_fb.h" +#include "phytium_fbdev.h" +#include "phytium_reg.h" +#include "phytium_pci.h" +#include "phytium_platform.h" +#include "phytium_debugfs.h" + +int dc_fake_mode_enable; +module_param(dc_fake_mode_enable, int, 0644); +MODULE_PARM_DESC(dc_fake_mode_enable, "Enable DC fake mode (0-disabled; 1-enabled; default-0)"); + +int dc_fast_training_check = 1; +module_param(dc_fast_training_check, int, 0644); +MODULE_PARM_DESC(dc_fast_training_check, "Check dp fast training (0-disabled; 1-enabled; default-1)"); + +int num_source_rates = 4; +module_param(num_source_rates, int, 0644); +MODULE_PARM_DESC(num_source_rates, "set the source max rates (1-1.62Gbps; 2-2.7Gbps; 3-5.4Gbps; 4-8.1Gbps; default-4)"); + +int source_max_lane_count = 4; +module_param(source_max_lane_count, int, 0644); +MODULE_PARM_DESC(source_max_lane_count, "set the source lane count (1-1lane; 2-2lane; 4-4lane; default-4)"); + +int link_dynamic_adjust; +module_param(link_dynamic_adjust, int, 0644); +MODULE_PARM_DESC(link_dynamic_adjust, "dynamic select the train pamameter according to the display mode (0-disabled; 1-enabled; default-1)"); + +int phytium_wait_cmd_done(struct phytium_display_private *priv, + uint32_t register_offset, + uint32_t request_bit, + uint32_t reply_bit) +{ + int timeout = 500, config = 0, ret = 0; + + do { + mdelay(1); + timeout--; + config = phytium_readl_reg(priv, 0, register_offset); + } while ((!(config & reply_bit)) && timeout); + + phytium_writel_reg(priv, config & (~request_bit), 0, register_offset); + + if (timeout == 0) { + DRM_ERROR("wait cmd reply timeout\n"); + ret = -EBUSY; + } else { + timeout = 500; + do { + mdelay(1); + timeout--; + config = phytium_readl_reg(priv, 0, register_offset); + } while ((config & reply_bit) && timeout); + if (timeout == 0) { + DRM_ERROR("clear cmd timeout\n"); + ret = -EBUSY; + } + } + mdelay(5); + + return ret; +} + +static void phytium_irq_preinstall(struct drm_device *dev) +{ + struct phytium_display_private *priv = dev->dev_private; + int i, status; + + for_each_pipe_masked(priv, i) { + status = phytium_readl_reg(priv, priv->dc_reg_base[i], PHYTIUM_DC_INT_STATUS); + phytium_writel_reg(priv, INT_DISABLE, priv->dc_reg_base[i], PHYTIUM_DC_INT_ENABLE); + } +} + +static void phytium_irq_uninstall(struct drm_device *dev) +{ + struct phytium_display_private *priv = dev->dev_private; + int i, status; + + for_each_pipe_masked(priv, i) { + status = phytium_readl_reg(priv, priv->dc_reg_base[i], PHYTIUM_DC_INT_STATUS); + phytium_writel_reg(priv, INT_DISABLE, priv->dc_reg_base[i], PHYTIUM_DC_INT_ENABLE); + } +} + +static irqreturn_t phytium_display_irq_handler(int irq, void *data) +{ + struct drm_device *dev = data; + struct phytium_display_private *priv = dev->dev_private; + bool enabled = 0; + int i = 0, virt_pipe = 0; + irqreturn_t ret = IRQ_NONE, ret1 = IRQ_NONE; + + for_each_pipe_masked(priv, i) { + enabled = phytium_readl_reg(priv, priv->dc_reg_base[i], PHYTIUM_DC_INT_STATUS); + if (enabled & INT_STATUS) { + virt_pipe = phytium_get_virt_pipe(priv, i); + if (virt_pipe < 0) + return IRQ_NONE; + drm_handle_vblank(dev, virt_pipe); + ret = IRQ_HANDLED; + if (priv->dc_hw_clear_msi_irq) + priv->dc_hw_clear_msi_irq(priv, i); + } + } + + ret1 = phytium_dp_hpd_irq_handler(priv); + if (ret == IRQ_HANDLED || ret1 == IRQ_HANDLED) + return IRQ_HANDLED; + + return IRQ_NONE; +} + +static const struct drm_mode_config_funcs phytium_mode_funcs = { + .fb_create = phytium_fb_create, + .output_poll_changed = drm_fb_helper_output_poll_changed, + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, +}; + +static void phytium_atomic_commit_tail(struct drm_atomic_state *state) +{ + struct drm_device *dev = state->dev; + + drm_atomic_helper_commit_modeset_disables(dev, state); + drm_atomic_helper_commit_planes(dev, state, false); + drm_atomic_helper_commit_modeset_enables(dev, state); + drm_atomic_helper_commit_hw_done(state); + drm_atomic_helper_wait_for_flip_done(dev, state); + drm_atomic_helper_cleanup_planes(dev, state); +} + +static struct drm_mode_config_helper_funcs phytium_mode_config_helpers = { + .atomic_commit_tail = phytium_atomic_commit_tail, +}; + +static int phytium_modeset_init(struct drm_device *dev) +{ + struct phytium_display_private *priv = dev->dev_private; + int i = 0, ret; + + drm_mode_config_init(dev); + dev->mode_config.min_width = 0; + dev->mode_config.min_height = 0; + dev->mode_config.max_width = 16384; + dev->mode_config.max_height = 16384; + dev->mode_config.cursor_width = 32; + dev->mode_config.cursor_height = 32; + + dev->mode_config.preferred_depth = 24; + dev->mode_config.prefer_shadow = 1; + dev->mode_config.allow_fb_modifiers = true; + + dev->mode_config.funcs = &phytium_mode_funcs; + dev->mode_config.helper_private = &phytium_mode_config_helpers; + + for_each_pipe_masked(priv, i) { + ret = phytium_crtc_init(dev, i); + if (ret) { + DRM_ERROR("phytium_crtc_init(pipe %d) return failed\n", i); + goto failed_crtc_init; + } + } + + for_each_pipe_masked(priv, i) { + ret = phytium_dp_init(dev, i); + if (ret) { + DRM_ERROR("phytium_dp_init(pipe %d) return failed\n", i); + goto failed_dp_init; + } + } + + drm_mode_config_reset(dev); + + return 0; +failed_dp_init: +failed_crtc_init: + drm_mode_config_cleanup(dev); + return ret; +} + +int phytium_get_virt_pipe(struct phytium_display_private *priv, int phys_pipe) +{ + int i = 0; + int virt_pipe = 0; + + for_each_pipe_masked(priv, i) { + if (i != phys_pipe) + virt_pipe++; + else + return virt_pipe; + } + + DRM_ERROR("%s %d failed\n", __func__, phys_pipe); + return -EINVAL; +} + +int phytium_get_phys_pipe(struct phytium_display_private *priv, int virt_pipe) +{ + int i = 0; + int tmp = 0; + + for_each_pipe_masked(priv, i) { + if (tmp != virt_pipe) + tmp++; + else + return i; + } + + DRM_ERROR("%s %d failed\n", __func__, virt_pipe); + return -EINVAL; +} + +static int phytium_display_load(struct drm_device *dev, unsigned long flags) +{ + struct phytium_display_private *priv = dev->dev_private; + int ret = 0; + + ret = drm_vblank_init(dev, priv->info.num_pipes); + if (ret) { + DRM_ERROR("vblank init failed\n"); + goto failed_vblank_init; + } + + ret = phytium_modeset_init(dev); + if (ret) { + DRM_ERROR("phytium_modeset_init failed\n"); + goto failed_modeset_init; + } + + if (priv->support_memory_type & MEMORY_TYPE_VRAM) + priv->vram_hw_init(priv); + + ret = drm_irq_install(dev, priv->irq); + if (ret) { + DRM_ERROR("install irq failed\n"); + goto failed_irq_install; + } + + ret = phytium_drm_fbdev_init(dev); + if (ret) + DRM_ERROR("failed to init dev\n"); + + phytium_debugfs_display_register(priv); + + return ret; + +failed_irq_install: + drm_mode_config_cleanup(dev); +failed_modeset_init: +failed_vblank_init: + return ret; +} + +static void phytium_display_unload(struct drm_device *dev) +{ + phytium_drm_fbdev_fini(dev); + drm_irq_uninstall(dev); + drm_mode_config_cleanup(dev); +} + +static const struct drm_ioctl_desc phytium_ioctls[] = { + /* for test, none so far */ +}; + +static const struct file_operations phytium_drm_driver_fops = { + .owner = THIS_MODULE, + .open = drm_open, + .release = drm_release, + .unlocked_ioctl = drm_ioctl, + .compat_ioctl = drm_compat_ioctl, + .poll = drm_poll, + .read = drm_read, + .llseek = no_llseek, + .mmap = phytium_gem_mmap, +}; + +struct drm_driver phytium_display_drm_driver = { + .driver_features = DRIVER_HAVE_IRQ | + DRIVER_MODESET | + DRIVER_ATOMIC | + DRIVER_GEM, + .load = phytium_display_load, + .unload = phytium_display_unload, + .lastclose = drm_fb_helper_lastclose, + .irq_handler = phytium_display_irq_handler, + .irq_preinstall = phytium_irq_preinstall, + .irq_uninstall = phytium_irq_uninstall, + .prime_handle_to_fd = drm_gem_prime_handle_to_fd, + .prime_fd_to_handle = drm_gem_prime_fd_to_handle, + .gem_prime_export = drm_gem_prime_export, + .gem_prime_import = drm_gem_prime_import, + .gem_prime_import_sg_table = phytium_gem_prime_import_sg_table, + .gem_prime_mmap = phytium_gem_prime_mmap, + .dumb_create = phytium_gem_dumb_create, + .dumb_destroy = phytium_gem_dumb_destroy, + .ioctls = phytium_ioctls, + .num_ioctls = ARRAY_SIZE(phytium_ioctls), + .fops = &phytium_drm_driver_fops, + .name = DRV_NAME, + .desc = DRV_DESC, + .date = DRV_DATE, + .major = DRV_MAJOR, + .minor = DRV_MINOR, +}; + +static void phytium_display_shutdown(struct drm_device *dev) +{ + drm_atomic_helper_shutdown(dev); +} + +static int phytium_display_pm_suspend(struct drm_device *dev) +{ + struct drm_atomic_state *state; + struct phytium_display_private *priv = dev->dev_private; + int ret, ret1; + + phytium_dp_hpd_irq_setup(dev, false); + cancel_work_sync(&priv->hotplug_work); + drm_fb_helper_set_suspend_unlocked(dev->fb_helper, 1); + state = drm_atomic_helper_suspend(dev); + if (IS_ERR(state)) { + DRM_ERROR("drm_atomic_helper_suspend failed: %ld\n", PTR_ERR(state)); + ret = PTR_ERR(state); + goto suspend_failed; + } + dev->mode_config.suspend_state = state; + ret = phytium_gem_suspend(dev); + if (ret) { + DRM_ERROR("phytium_gem_suspend failed: %d\n", ret); + goto gem_suspend_failed; + } + + return 0; + +gem_suspend_failed: + ret1 = drm_atomic_helper_resume(dev, dev->mode_config.suspend_state); + if (ret1) + DRM_ERROR("Failed to resume (%d)\n", ret1); + dev->mode_config.suspend_state = NULL; +suspend_failed: + drm_fb_helper_set_suspend_unlocked(dev->fb_helper, 0); + phytium_dp_hpd_irq_setup(dev, true); + + return ret; +} + +static int phytium_display_pm_resume(struct drm_device *dev) +{ + struct phytium_display_private *priv = dev->dev_private; + int ret = 0; + + if (WARN_ON(!dev->mode_config.suspend_state)) + return -EINVAL; + + ret = phytium_dp_resume(dev); + if (ret) + return -EIO; + + phytium_crtc_resume(dev); + phytium_gem_resume(dev); + + if (priv->support_memory_type & MEMORY_TYPE_VRAM) + priv->vram_hw_init(priv); + + ret = drm_atomic_helper_resume(dev, dev->mode_config.suspend_state); + if (ret) { + DRM_ERROR("Failed to resume (%d)\n", ret); + return ret; + } + + dev->mode_config.suspend_state = NULL; + drm_fb_helper_set_suspend_unlocked(dev->fb_helper, 0); + phytium_dp_hpd_irq_setup(dev, true); + + return 0; +} + +void phytium_display_private_init(struct phytium_display_private *priv, struct drm_device *dev) +{ + INIT_LIST_HEAD(&priv->gem_list_head); + spin_lock_init(&priv->hotplug_irq_lock); + INIT_WORK(&priv->hotplug_work, phytium_dp_hpd_work_func); + memset(priv->mem_state, 0, sizeof(priv->mem_state)); + priv->dev = dev; + priv->display_shutdown = phytium_display_shutdown; + priv->display_pm_suspend = phytium_display_pm_suspend; + priv->display_pm_resume = phytium_display_pm_resume; +} + +static int __init phytium_display_init(void) +{ + int ret = 0; + + ret = platform_driver_register(&phytium_platform_driver); + if (ret) + return ret; + + ret = pci_register_driver(&phytium_pci_driver); + + return ret; +} + +static void __exit phytium_display_exit(void) +{ + pci_unregister_driver(&phytium_pci_driver); + + platform_driver_unregister(&phytium_platform_driver); +} + +module_init(phytium_display_init); +module_exit(phytium_display_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Yang Xun "); +MODULE_AUTHOR("Shaojun Yang "); +MODULE_DESCRIPTION("Phytium Display Controller"); diff --git a/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_display_drv.h b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_display_drv.h new file mode 100644 index 000000000..ee1d0e0fa --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_display_drv.h @@ -0,0 +1,178 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Phytium display drm driver + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#ifndef __PHYTIUM_DISPLAY_DRV_H__ +#define __PHYTIUM_DISPLAY_DRV_H__ + +#include +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)) +#include +#endif +#include +#include + +#define DEBUG_LOG 0 + +#define PHYTIUM_FORMAT_MAX_PLANE 3 +#define DP_MAX_DOWNSTREAM_PORTS 0x10 + +#define DRV_NAME "dc" +#define DRV_DESC "phytium dc" +#define DRV_DATE "20201220" +#define DRV_MAJOR 1 +#define DRV_MINOR 1 + +/* come from GPU */ +#define DRM_FORMAT_MOD_VENDOR_PHYTIUM 0x92 + +/* dc:mode0 8x8 16bpp gpu: FBCDC_8X8_V10 */ +#define DRM_FORMAT_MOD_PHYTIUM_TILE_MODE0_FBCDC fourcc_mod_code(PHYTIUM, 21) +/* dc:mode3 8x4 32bpp gpu: FBCDC_16X4_v10 */ +#define DRM_FORMAT_MOD_PHYTIUM_TILE_MODE3_FBCDC fourcc_mod_code(PHYTIUM, 22) + +#define PIPE_MASK_SHIFT 0x0 +#define PIPE_MASK_MASK 0x7 +#define EDP_MASK_SHIFT 0x3 +#define EDP_MASK_MASK 0x7 + +enum phytium_platform { + PHYTIUM_PLATFORM_UNINITIALIZED = 0, + PHYTIUM_PLATFORM_PX210, + PHYTIUM_PLATFORM_PE220X, +}; + +enum phytium_mem_state_type { + PHYTIUM_MEM_VRAM_TOTAL = 0, + PHYTIUM_MEM_VRAM_ALLOC, + PHYTIUM_MEM_SYSTEM_CARVEOUT_TOTAL, + PHYTIUM_MEM_SYSTEM_CARVEOUT_ALLOC, + PHYTIUM_MEM_SYSTEM_UNIFIED_ALLOC, + PHYTIUM_MEM_STATE_TYPE_COUNT, +}; + +#define MEMORY_TYPE_VRAM 0x1 +#define MEMORY_TYPE_SYSTEM_CARVEOUT 0x2 +#define MEMORY_TYPE_SYSTEM_UNIFIED 0x4 + +#define IS_PLATFORM(priv, p) ((priv)->info.platform_mask & BIT(p)) + +#define IS_PX210(priv) IS_PLATFORM(priv, PHYTIUM_PLATFORM_PX210) +#define IS_PE220X(priv) IS_PLATFORM(priv, PHYTIUM_PLATFORM_PE220X) + +struct phytium_device_info { + unsigned char platform_mask; + unsigned char pipe_mask; + unsigned char num_pipes; + unsigned char total_pipes; + unsigned char edp_mask; + unsigned int crtc_clock_max; + unsigned int hdisplay_max; + unsigned int vdisplay_max; + unsigned int backlight_max; + unsigned long address_mask; +}; + +struct phytium_display_private { + /* hw */ + void __iomem *regs; + void __iomem *vram_addr; + struct phytium_device_info info; + char support_memory_type; + char reserve[3]; + uint32_t dc_reg_base[3]; + uint32_t dcreq_reg_base[3]; + uint32_t dp_reg_base[3]; + uint32_t address_transform_base; + uint32_t phy_access_base[3]; + + /* drm */ + struct drm_device *dev; + int irq; + + /* fb_dev */ + struct drm_fb_helper fbdev_helper; + struct phytium_gem_object *fbdev_phytium_gem; + + int save_reg[3]; + struct list_head gem_list_head; + + struct work_struct hotplug_work; + spinlock_t hotplug_irq_lock; + + void (*vram_hw_init)(struct phytium_display_private *priv); + void (*display_shutdown)(struct drm_device *dev); + int (*display_pm_suspend)(struct drm_device *dev); + int (*display_pm_resume)(struct drm_device *dev); + void (*dc_hw_clear_msi_irq)(struct phytium_display_private *priv, uint32_t phys_pipe); + int (*dc_hw_fb_format_check)(const struct drm_mode_fb_cmd2 *mode_cmd, int count); + + struct gen_pool *memory_pool; + resource_size_t pool_phys_addr; + resource_size_t pool_size; + void *pool_virt_addr; + uint64_t mem_state[PHYTIUM_MEM_STATE_TYPE_COUNT]; + + /* DMA info */ + int dma_inited; + struct dma_chan *dma_chan; +}; + +static inline unsigned int +phytium_readl_reg(struct phytium_display_private *priv, uint32_t group_offset, uint32_t reg_offset) +{ + unsigned int data; + + data = readl(priv->regs + group_offset + reg_offset); +#if DEBUG_LOG + pr_info("Read 32'h%08x 32'h%08x\n", group_offset + reg_offset, data); +#endif + return data; +} + +static inline void +phytium_writel_reg(struct phytium_display_private *priv, uint32_t data, + uint32_t group_offset, uint32_t reg_offset) +{ + + writel(data, priv->regs + group_offset + reg_offset); +#if DEBUG_LOG + pr_info("Write 32'h%08x 32'h%08x\n", group_offset + reg_offset, data); +#endif +} + +static inline void +phytium_writeb_reg(struct phytium_display_private *priv, uint8_t data, + uint32_t group_offset, uint32_t reg_offset) +{ + writeb(data, priv->regs + group_offset + reg_offset); +#if DEBUG_LOG + pr_info("Write 32'h%08x 8'h%08x\n", group_offset + reg_offset, data); +#endif +} + +#define for_each_pipe(__dev_priv, __p) \ + for ((__p) = 0; (__p) < __dev_priv->info.total_pipes; (__p)++) + +#define for_each_pipe_masked(__dev_priv, __p) \ + for ((__p) = 0; (__p) < __dev_priv->info.total_pipes; (__p)++) \ + for_each_if((__dev_priv->info.pipe_mask) & BIT(__p)) + +int phytium_get_virt_pipe(struct phytium_display_private *priv, int phys_pipe); +int phytium_get_phys_pipe(struct phytium_display_private *priv, int virt_pipe); +int phytium_wait_cmd_done(struct phytium_display_private *priv, + uint32_t register_offset, + uint32_t request_bit, + uint32_t reply_bit); +void phytium_display_private_init(struct phytium_display_private *priv, struct drm_device *dev); + +extern struct drm_driver phytium_display_drm_driver; +extern int dc_fake_mode_enable; +extern int dc_fast_training_check; +extern int num_source_rates; +extern int source_max_lane_count; +extern int link_dynamic_adjust; + +#endif /* __PHYTIUM_DISPLAY_DRV_H__ */ diff --git a/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_dp.c b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_dp.c new file mode 100644 index 000000000..96fe440cd --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_dp.c @@ -0,0 +1,2662 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Phytium display drm driver + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)) +#include +#endif +#include "phytium_display_drv.h" +#include "phytium_dp.h" +#include "phytium_debugfs.h" +#include "px210_dp.h" +#include "pe220x_dp.h" +#include "phytium_panel.h" +#include "phytium_reg.h" + +static void phytium_dp_aux_init(struct phytium_dp_device *phytium_dp); +static void handle_plugged_change(struct phytium_dp_device *phytium_dp, bool plugged); +static bool phytium_edp_init_connector(struct phytium_dp_device *phytium_dp); +static void phytium_edp_fini_connector(struct phytium_dp_device *phytium_dp); +static void phytium_edp_panel_poweroff(struct phytium_dp_device *phytium_dp); +static void phytium_dp_audio_codec_fini(struct phytium_dp_device *phytium_dp); + +static int phytium_rate[] = {162000, 270000, 540000, 810000}; +static int codec_id = PHYTIUM_DP_AUDIO_ID; + +void phytium_phy_writel(struct phytium_dp_device *phytium_dp, uint32_t address, uint32_t data) +{ + struct drm_device *dev = phytium_dp->dev; + struct phytium_display_private *priv = dev->dev_private; + int port = phytium_dp->port; + uint32_t group_offset = priv->phy_access_base[port]; + +#if DEBUG_LOG + pr_info("phy address write: 0x%x data:0x%x\n", address, data); +#endif + phytium_writel_reg(priv, address, group_offset, PHYTIUM_PHY_ACCESS_ADDRESS); + phytium_writel_reg(priv, data, group_offset, PHYTIUM_PHY_WRITE_DATA); + phytium_writel_reg(priv, ACCESS_WRITE, group_offset, PHYTIUM_PHY_ACCESS_CTRL); + udelay(10); +} + +uint32_t phytium_phy_readl(struct phytium_dp_device *phytium_dp, uint32_t address) +{ + struct drm_device *dev = phytium_dp->dev; + struct phytium_display_private *priv = dev->dev_private; + int port = phytium_dp->port; + uint32_t group_offset = priv->phy_access_base[port]; + uint32_t data; + + phytium_writel_reg(priv, address, group_offset, PHYTIUM_PHY_ACCESS_ADDRESS); + phytium_writel_reg(priv, ACCESS_READ, group_offset, PHYTIUM_PHY_ACCESS_CTRL); + udelay(10); + data = phytium_readl_reg(priv, group_offset, PHYTIUM_PHY_READ_DATA); +#if DEBUG_LOG + pr_info("phy address read: 0x%x data:0x%x\n", address, data); +#endif + + return data; +} + +static int +phytium_dp_hw_aux_transfer_write(struct phytium_dp_device *phytium_dp, struct drm_dp_aux_msg *msg) +{ + struct drm_device *dev = phytium_dp->dev; + struct phytium_display_private *priv = dev->dev_private; + int port = phytium_dp->port; + uint32_t group_offset = priv->dp_reg_base[port]; + unsigned int i = 0, j = 0; + unsigned int cmd = 0; + unsigned int aux_status = 0, interrupt_status = 0; + unsigned char *data = msg->buffer; + int count_timeout = 0; + long ret = 0; + + for (i = 0; i < 3; i++) { + /* clear PX210_DP_INTERRUPT_RAW_STATUS */ + phytium_readl_reg(priv, group_offset, PHYTIUM_DP_INTERRUPT_STATUS); + phytium_writel_reg(priv, msg->address, group_offset, PHYTIUM_DP_AUX_ADDRESS); + for (j = 0; j < msg->size; j++) + phytium_writeb_reg(priv, data[j], group_offset, PHYTIUM_DP_AUX_WRITE_FIFO); + + cmd = ((msg->request & COMMAND_MASK) << COMMAND_SHIFT); + if (msg->size == 0) + cmd |= ADDRESS_ONLY; + else + cmd |= (msg->size-1) & BYTE_COUNT_MASK; + phytium_writel_reg(priv, cmd, group_offset, PHYTIUM_DP_AUX_COMMAND); + + count_timeout = 0; + do { + mdelay(5); + interrupt_status = phytium_readl_reg(priv, group_offset, + PHYTIUM_DP_INTERRUPT_RAW_STATUS); + aux_status = phytium_readl_reg(priv, group_offset, PHYTIUM_DP_AUX_STATUS); + if ((aux_status & REPLY_RECEIVED) || (aux_status & REPLY_ERROR) + || (interrupt_status & REPLY_TIMEOUT)) { + DRM_DEBUG_KMS("aux wait exit\n"); + break; + } + count_timeout++; + } while (count_timeout < 6); + + phytium_readl_reg(priv, group_offset, PHYTIUM_DP_INTERRUPT_STATUS); + if (interrupt_status & REPLY_TIMEOUT) { + DRM_DEBUG_KMS("aux write reply timeout\n"); + continue; + } else if (aux_status & REPLY_ERROR) { + DRM_DEBUG_KMS("aux write reply error\n"); + continue; + } else if (aux_status & REPLY_RECEIVED) { + DRM_DEBUG_KMS("aux write reply received succussful\n"); + break; + } + } + + if (interrupt_status & REPLY_TIMEOUT) { + DRM_NOTE("aux(%d) write reply timeout\n", phytium_dp->port); + ret = -EIO; + goto out; + } else if (aux_status & REPLY_ERROR) { + DRM_ERROR("aux(%d) write reply error\n", phytium_dp->port); + ret = -EIO; + goto out; + } else if ((aux_status & REPLY_RECEIVED) != REPLY_RECEIVED) { + DRM_ERROR("aux(%d) write reply no response\n", phytium_dp->port); + ret = -EIO; + goto out; + } + + msg->reply = phytium_readl_reg(priv, group_offset, PHYTIUM_DP_AUX_REPLY_CODE); + ret = msg->size; +out: + return ret; +} + +static int +phytium_dp_hw_aux_transfer_read(struct phytium_dp_device *phytium_dp, struct drm_dp_aux_msg *msg) +{ + struct drm_device *dev = phytium_dp->dev; + struct phytium_display_private *priv = dev->dev_private; + int port = phytium_dp->port; + uint32_t group_offset = priv->dp_reg_base[port]; + unsigned int i = 0; + unsigned int cmd = 0; + unsigned int aux_status = 0, interrupt_status = 0; + unsigned char *data = msg->buffer; + int count_timeout = 0; + long ret = 0; + + for (i = 0; i < 3; i++) { + phytium_readl_reg(priv, group_offset, PHYTIUM_DP_INTERRUPT_STATUS); + phytium_writel_reg(priv, msg->address, group_offset, PHYTIUM_DP_AUX_ADDRESS); + cmd = ((msg->request & COMMAND_MASK) << COMMAND_SHIFT); + if (msg->size == 0) + cmd |= ADDRESS_ONLY; + else + cmd |= ((msg->size-1) & BYTE_COUNT_MASK); + phytium_writel_reg(priv, cmd, group_offset, PHYTIUM_DP_AUX_COMMAND); + + count_timeout = 0; + do { + mdelay(5); + interrupt_status = phytium_readl_reg(priv, group_offset, + PHYTIUM_DP_INTERRUPT_RAW_STATUS); + aux_status = phytium_readl_reg(priv, group_offset, PHYTIUM_DP_AUX_STATUS); + if ((aux_status & REPLY_RECEIVED) || (aux_status & REPLY_ERROR) + || (interrupt_status & REPLY_TIMEOUT)) { + DRM_DEBUG_KMS("aux wait exit\n"); + break; + } + count_timeout++; + } while (count_timeout < 6); + + phytium_readl_reg(priv, group_offset, PHYTIUM_DP_INTERRUPT_STATUS); + if (interrupt_status & REPLY_TIMEOUT) { + DRM_DEBUG_KMS("aux read reply timeout\n"); + continue; + } else if (aux_status & REPLY_ERROR) { + DRM_DEBUG_KMS("aux read reply error\n"); + continue; + } else if (aux_status & REPLY_RECEIVED) { + DRM_DEBUG_KMS("aux read reply received succussful\n"); + break; + } + } + + if (interrupt_status & REPLY_TIMEOUT) { + DRM_NOTE("aux(%d) read reply timeout\n", phytium_dp->port); + ret = -EIO; + goto out; + } else if (aux_status & REPLY_ERROR) { + DRM_ERROR("aux(%d) read reply error\n", phytium_dp->port); + ret = -EIO; + goto out; + } else if ((aux_status & REPLY_RECEIVED) != REPLY_RECEIVED) { + DRM_ERROR("aux(%d) read reply no response\n", phytium_dp->port); + ret = -EIO; + goto out; + } + + msg->reply = phytium_readl_reg(priv, group_offset, PHYTIUM_DP_AUX_REPLY_CODE); + ret = phytium_readl_reg(priv, group_offset, PHYTIUM_DP_AUX_REPLY_DATA_COUNT); + + if (ret > msg->size) { + ret = msg->size; + } else if (ret != msg->size) { + DRM_DEBUG_KMS("aux read count error(ret:0x%lx != 0x%lx)\n", ret, msg->size); + ret = -EBUSY; + goto out; + } + + for (i = 0; i < ret; i++) + data[i] = phytium_readl_reg(priv, group_offset, PHYTIUM_DP_AUX_REPLY_DATA); + +out: + return ret; +} + +static void phytium_get_native_mode(struct phytium_dp_device *phytium_dp) +{ + struct drm_display_mode *t, *mode; + struct drm_connector *connector = &phytium_dp->connector; + struct drm_display_mode *native_mode = &phytium_dp->native_mode; + + list_for_each_entry_safe(mode, t, &connector->probed_modes, head) { + if (mode->type & DRM_MODE_TYPE_PREFERRED) { + if (mode->hdisplay != native_mode->hdisplay || + mode->vdisplay != native_mode->vdisplay) { + memcpy(native_mode, mode, sizeof(*mode)); + drm_mode_set_crtcinfo(native_mode, 0); + } + break; + } + } + + if (&mode->head == &connector->probed_modes) + native_mode->clock = 0; +} + +static int phytium_connector_add_common_modes(struct phytium_dp_device *phytium_dp) +{ + int i = 0, ret = 0; + struct drm_device *dev = phytium_dp->dev; + struct drm_display_mode *mode = NULL, *current_mode = NULL; + struct drm_display_mode *native_mode = &phytium_dp->native_mode; + bool mode_existed = false; + struct mode_size { + char name[DRM_DISPLAY_MODE_LEN]; + int w; + int h; + } common_mode[] = { + { "640x480", 640, 480}, + { "800x600", 800, 600}, + { "1024x768", 1024, 768}, + { "1280x720", 1280, 720}, + { "1280x800", 1280, 800}, + {"1280x1024", 1280, 1024}, + { "1440x900", 1440, 900}, + {"1680x1050", 1680, 1050}, + {"1600x1200", 1600, 1200}, + {"1920x1080", 1920, 1080}, + {"1920x1200", 1920, 1200} + }; + + if (native_mode->clock == 0) + return ret; + + for (i = 0; i < ARRAY_SIZE(common_mode); i++) { + mode_existed = false; + + if (common_mode[i].w > native_mode->hdisplay || + common_mode[i].h > native_mode->vdisplay || + (common_mode[i].w == native_mode->hdisplay && + common_mode[i].h == native_mode->vdisplay)) + continue; + + list_for_each_entry(current_mode, &phytium_dp->connector.probed_modes, head) { + if (common_mode[i].w == current_mode->hdisplay && + common_mode[i].h == current_mode->vdisplay) { + mode_existed = true; + break; + } + } + + if (mode_existed) + continue; + + mode = drm_mode_duplicate(dev, native_mode); + if (mode == NULL) + continue; + + mode->hdisplay = common_mode[i].w; + mode->vdisplay = common_mode[i].h; + mode->type &= ~DRM_MODE_TYPE_PREFERRED; + strncpy(mode->name, common_mode[i].name, DRM_DISPLAY_MODE_LEN); + drm_mode_probed_add(&phytium_dp->connector, mode); + ret++; + } + + return ret; +} + +static int phytium_connector_get_modes(struct drm_connector *connector) +{ + struct phytium_dp_device *phytium_dp = connector_to_dp_device(connector); + struct edid *edid; + int ret = 0; + + if (phytium_dp->is_edp) + edid = phytium_dp->edp_edid; + else + edid = drm_get_edid(connector, &phytium_dp->aux.ddc); + + if (edid && drm_edid_is_valid(edid)) { + drm_connector_update_edid_property(connector, edid); + ret = drm_add_edid_modes(connector, edid); + phytium_dp->has_audio = drm_detect_monitor_audio(edid); + phytium_get_native_mode(phytium_dp); + if (dc_fake_mode_enable) + ret += phytium_connector_add_common_modes(phytium_dp); + } else { + drm_connector_update_edid_property(connector, NULL); + phytium_dp->has_audio = false; + } + + if (!phytium_dp->is_edp) + kfree(edid); + + return ret; +} + +static struct drm_encoder *phytium_dp_best_encoder(struct drm_connector *connector) +{ + struct phytium_dp_device *phytium_dp = connector_to_dp_device(connector); + + return &phytium_dp->encoder; +} + +static const +struct drm_connector_helper_funcs phytium_connector_helper_funcs = { + .get_modes = phytium_connector_get_modes, + .best_encoder = phytium_dp_best_encoder, +}; + +static void phytium_dp_set_sink_rates(struct phytium_dp_device *phytium_dp) +{ + static const int dp_rates[] = {162000, 270000, 540000, 810000}; + int i, max_rate; + + max_rate = drm_dp_bw_code_to_link_rate(phytium_dp->dpcd[DP_MAX_LINK_RATE]); + for (i = 0; i < ARRAY_SIZE(dp_rates); i++) { + if (dp_rates[i] > max_rate) + break; + phytium_dp->sink_rates[i] = dp_rates[i]; + } + phytium_dp->num_sink_rates = i; +} + +static int get_common_rates(const int *source_rates, int source_len, const int *sink_rates, + int sink_len, int *common_rates) +{ + int i = 0, j = 0, k = 0; + + while (i < source_len && j < sink_len) { + if (source_rates[i] == sink_rates[j]) { + if (WARN_ON(k >= DP_MAX_SUPPORTED_RATES)) + return k; + common_rates[k] = source_rates[i]; + ++k; + ++i; + ++j; + } else if (source_rates[i] < sink_rates[j]) { + ++i; + } else { + ++j; + } + } + return k; +} + +static void phytium_dp_set_common_rates(struct phytium_dp_device *phytium_dp) +{ + WARN_ON(!phytium_dp->num_source_rates || !phytium_dp->num_sink_rates); + + phytium_dp->num_common_rates = get_common_rates(phytium_dp->source_rates, + phytium_dp->num_source_rates, + phytium_dp->sink_rates, + phytium_dp->num_sink_rates, + phytium_dp->common_rates); + + if (WARN_ON(phytium_dp->num_common_rates == 0)) { + phytium_dp->common_rates[0] = 162000; + phytium_dp->num_common_rates = 1; + } +} + +static bool phytium_dp_get_dpcd(struct phytium_dp_device *phytium_dp) +{ + int ret; + unsigned char sink_count = 0; + + /* get dpcd capability,but don't check data error; so check revision */ + ret = drm_dp_dpcd_read(&phytium_dp->aux, 0x00, phytium_dp->dpcd, + sizeof(phytium_dp->dpcd)); + if (ret < 0) { + DRM_ERROR("port %d get DPCD capability fail\n", phytium_dp->port); + return false; + } + + if (phytium_dp->dpcd[DP_DPCD_REV] == 0) { + DRM_ERROR("DPCD data error: 0x%x\n", phytium_dp->dpcd[DP_DPCD_REV]); + return false; + } + + /* parse sink support link */ + phytium_dp_set_sink_rates(phytium_dp); + phytium_dp_set_common_rates(phytium_dp); + phytium_dp->sink_max_lane_count = drm_dp_max_lane_count(phytium_dp->dpcd); + phytium_dp->common_max_lane_count = min(phytium_dp->source_max_lane_count, + phytium_dp->sink_max_lane_count); + + /* get dpcd sink count */ + if (drm_dp_dpcd_readb(&phytium_dp->aux, DP_SINK_COUNT, &sink_count) <= 0) { + DRM_ERROR("get DPCD sink_count fail\n"); + return false; + } + + phytium_dp->sink_count = DP_GET_SINK_COUNT(sink_count); + if (!phytium_dp->sink_count) { + DRM_ERROR("DPCD sink_count should not be zero\n"); + return false; + } + + if (!drm_dp_is_branch(phytium_dp->dpcd)) + return true; + + if (phytium_dp->dpcd[DP_DPCD_REV] == 0x10) + return true; + + /* get downstream port for branch device */ + ret = drm_dp_dpcd_read(&phytium_dp->aux, DP_DOWNSTREAM_PORT_0, + phytium_dp->downstream_ports, DP_MAX_DOWNSTREAM_PORTS); + if (ret < 0) { + DRM_ERROR("get DPCD DFP fail\n"); + return false; + } + + return true; +} + +static enum drm_connector_status +phytium_dp_detect_dpcd(struct phytium_dp_device *phytium_dp) +{ + if (!phytium_dp_get_dpcd(phytium_dp)) + return connector_status_disconnected; + + if (!drm_dp_is_branch(phytium_dp->dpcd)) + return connector_status_connected; + + if (phytium_dp->downstream_ports[0] & DP_DS_PORT_HPD) { + return phytium_dp->sink_count ? connector_status_connected + : connector_status_disconnected; + } + return connector_status_connected; +} + +static void phytium_get_adjust_train(struct phytium_dp_device *phytium_dp, + const uint8_t link_status[DP_LINK_STATUS_SIZE], uint8_t lane_count) +{ + unsigned char v = 0; + unsigned char p = 0; + int lane; + unsigned char voltage_max; + unsigned char preemph_max; + + /* find max value */ + for (lane = 0; lane < lane_count; lane++) { + uint8_t this_v = drm_dp_get_adjust_request_voltage(link_status, lane); + uint8_t this_p = drm_dp_get_adjust_request_pre_emphasis(link_status, lane); + + if (this_v > v) + v = this_v; + if (this_p > p) + p = this_p; + } + voltage_max = DP_TRAIN_VOLTAGE_SWING_LEVEL_3; + if (v >= voltage_max) + v = voltage_max | DP_TRAIN_MAX_SWING_REACHED; + + preemph_max = DP_TRAIN_PRE_EMPH_LEVEL_3; + if (p >= preemph_max) + p = preemph_max | DP_TRAIN_MAX_PRE_EMPHASIS_REACHED; + + for (lane = 0; lane < 4; lane++) + phytium_dp->train_set[lane] = v | p; +} + +bool phytium_dp_coding_8b10b_need_enable(unsigned char test_pattern) +{ + switch (test_pattern) { + case PHYTIUM_PHY_TP_D10_2: + case PHYTIUM_PHY_TP_SYMBOL_ERROR: + case PHYTIUM_PHY_TP_CP2520_1: + case PHYTIUM_PHY_TP_CP2520_2: + case PHYTIUM_PHY_TP_CP2520_3: + return true; + case PHYTIUM_PHY_TP_PRBS7: + case PHYTIUM_PHY_TP_80BIT_CUSTOM: + return false; + default: + return false; + } +} + +bool phytium_dp_scrambled_need_enable(unsigned char test_pattern) +{ + switch (test_pattern) { + case PHYTIUM_PHY_TP_SYMBOL_ERROR: + case PHYTIUM_PHY_TP_CP2520_1: + case PHYTIUM_PHY_TP_CP2520_2: + case PHYTIUM_PHY_TP_CP2520_3: + return true; + case PHYTIUM_PHY_TP_D10_2: + case PHYTIUM_PHY_TP_PRBS7: + case PHYTIUM_PHY_TP_80BIT_CUSTOM: + return false; + default: + return false; + } +} + +static void phytium_dp_hw_set_lane_setting(struct phytium_dp_device *phytium_dp, + uint32_t link_rate, + uint8_t train_set) +{ + phytium_dp->funcs->dp_hw_set_phy_lane_setting(phytium_dp, link_rate, train_set); +} + +static void phytium_dp_hw_set_link(struct phytium_dp_device *phytium_dp, + uint8_t lane_count, + uint32_t link_rate) +{ + struct drm_device *dev = phytium_dp->dev; + struct phytium_display_private *priv = dev->dev_private; + int port = phytium_dp->port, ret = 0, retry = 3; + uint32_t group_offset = priv->dp_reg_base[port]; + + phytium_writel_reg(priv, lane_count, + group_offset, PHYTIUM_DP_LANE_COUNT_SET); + phytium_writel_reg(priv, + drm_dp_link_rate_to_bw_code(link_rate), + group_offset, PHYTIUM_DP_LINK_BW_SET); + + if (drm_dp_enhanced_frame_cap(phytium_dp->dpcd)) + phytium_writel_reg(priv, ENHANCED_FRAME_ENABLE, + group_offset, PHYTIUM_DP_ENHANCED_FRAME_EN); + else + phytium_writel_reg(priv, ENHANCED_FRAME_DISABLE, + group_offset, PHYTIUM_DP_ENHANCED_FRAME_EN); + +try_again: + ret = phytium_dp->funcs->dp_hw_set_phy_lane_and_rate(phytium_dp, lane_count, link_rate); + if ((ret < 0) && retry) { + retry--; + goto try_again; + } +} + +static void phytium_dp_hw_set_test_pattern(struct phytium_dp_device *phytium_dp, + uint8_t lane_count, + uint8_t test_pattern, + uint8_t *custom_pattern, + uint32_t custom_pattern_size) +{ + struct drm_device *dev = phytium_dp->dev; + struct phytium_display_private *priv = dev->dev_private; + int port = phytium_dp->port, val = 0, tmp = 0, i; + uint32_t group_offset = priv->dp_reg_base[port]; + + if ((test_pattern == PHYTIUM_PHY_TP_80BIT_CUSTOM) + && custom_pattern && (custom_pattern_size > 0)) { + val = *(int *)custom_pattern; + phytium_writel_reg(priv, val, group_offset, PHYTIUM_DP_CUSTOM_80BIT_PATTERN_0); + val = *(int *)(custom_pattern + 4); + phytium_writel_reg(priv, val, group_offset, PHYTIUM_DP_CUSTOM_80BIT_PATTERN_1); + val = *(short int *)(custom_pattern + 8); + phytium_writel_reg(priv, val, group_offset, PHYTIUM_DP_CUSTOM_80BIT_PATTERN_2); + } + + if (test_pattern == PHYTIUM_PHY_TP_D10_2 || test_pattern == PHYTIUM_PHY_TP_PRBS7 + || test_pattern == PHYTIUM_PHY_TP_80BIT_CUSTOM) + phytium_writel_reg(priv, SCRAMBLING_DISABLE, group_offset, + PHYTIUM_DP_SCRAMBLING_DISABLE); + else + phytium_writel_reg(priv, SCRAMBLING_ENABLE, group_offset, + PHYTIUM_DP_SCRAMBLING_DISABLE); + + tmp = test_pattern - PHYTIUM_PHY_TP_NONE + TEST_PATTERN_NONE; + val = 0; + for (i = 0; i < lane_count; i++) + val |= (tmp << (TEST_PATTERN_LANE_SHIFT * i)); + phytium_writel_reg(priv, val, group_offset, PHYTIUM_DP_LINK_QUAL_PATTERN_SET); +} + +static void phytium_dp_hw_set_train_pattern(struct phytium_dp_device *phytium_dp, + uint8_t train_pattern) +{ + struct drm_device *dev = phytium_dp->dev; + struct phytium_display_private *priv = dev->dev_private; + int port = phytium_dp->port, tmp = 0; + uint32_t group_offset = priv->dp_reg_base[port]; + + /* Scrambling is disabled for TPS1/TPS2/3 and enabled for TPS4 */ + if (train_pattern == DP_TRAINING_PATTERN_4 + || train_pattern == DP_TRAINING_PATTERN_DISABLE) { + phytium_writel_reg(priv, SCRAMBLING_ENABLE, group_offset, + PHYTIUM_DP_SCRAMBLING_DISABLE); + phytium_writel_reg(priv, SCRAMBLER_RESET, group_offset, + PHYTIUM_DP_FORCE_SCRAMBLER_RESET); + } else { + phytium_writel_reg(priv, SCRAMBLING_DISABLE, group_offset, + PHYTIUM_DP_SCRAMBLING_DISABLE); + } + switch (train_pattern) { + case DP_TRAINING_PATTERN_DISABLE: + tmp = TRAINING_OFF; + break; + case DP_TRAINING_PATTERN_1: + tmp = TRAINING_PATTERN_1; + break; + case DP_TRAINING_PATTERN_2: + tmp = TRAINING_PATTERN_2; + break; + case DP_TRAINING_PATTERN_3: + tmp = TRAINING_PATTERN_3; + break; + case DP_TRAINING_PATTERN_4: + tmp = TRAINING_PATTERN_4; + break; + default: + tmp = TRAINING_OFF; + break; + } + + phytium_writel_reg(priv, tmp, group_offset, PHYTIUM_DP_TRAINING_PATTERN_SET); +} + +void phytium_dp_hw_enable_audio(struct phytium_dp_device *phytium_dp) +{ + struct drm_device *dev = phytium_dp->dev; + struct phytium_display_private *priv = dev->dev_private; + int port = phytium_dp->port; + int config = 0, config1, data_window = 0; + const struct dp_audio_n_m *n_m = NULL; + uint32_t group_offset = priv->dp_reg_base[port]; + + config = phytium_readl_reg(priv, group_offset, PHYTIUM_DP_SEC_AUDIO_ENABLE); + phytium_writel_reg(priv, CHANNEL_MUTE_ENABLE, group_offset, PHYTIUM_DP_SEC_AUDIO_ENABLE); + + data_window = 90*(phytium_dp->link_rate)/100 + *(phytium_dp->mode.htotal - phytium_dp->mode.hdisplay) + /phytium_dp->mode.clock/4; + + phytium_writel_reg(priv, data_window, group_offset, PHYTIUM_DP_SEC_DATA_WINDOW); + + n_m = phytium_dp_audio_get_n_m(phytium_dp->link_rate, phytium_dp->audio_info.sample_rate); + if (n_m == NULL) { + DRM_NOTE("can not get n_m for link_rate(%d) and sample_rate(%d)\n", + phytium_dp->link_rate, phytium_dp->audio_info.sample_rate); + phytium_writel_reg(priv, 0, group_offset, PHYTIUM_DP_SEC_MAUD); + phytium_writel_reg(priv, 0, group_offset, PHYTIUM_DP_SEC_NAUD); + } else { + phytium_writel_reg(priv, n_m->m, group_offset, PHYTIUM_DP_SEC_MAUD); + phytium_writel_reg(priv, n_m->n, group_offset, PHYTIUM_DP_SEC_NAUD); + } + + config1 = phytium_readl_reg(priv, group_offset, PHYTIUM_DP_SECONDARY_STREAM_ENABLE); + phytium_writel_reg(priv, SECONDARY_STREAM_DISABLE, + group_offset, PHYTIUM_DP_SECONDARY_STREAM_ENABLE); + phytium_writel_reg(priv, config1, group_offset, PHYTIUM_DP_SECONDARY_STREAM_ENABLE); + phytium_writel_reg(priv, config, group_offset, PHYTIUM_DP_SEC_AUDIO_ENABLE); +} + +static void phytium_dp_hw_audio_shutdown(struct phytium_dp_device *phytium_dp) +{ + struct drm_device *dev = phytium_dp->dev; + struct phytium_display_private *priv = dev->dev_private; + int port = phytium_dp->port; + uint32_t group_offset = priv->dp_reg_base[port]; + + phytium_writel_reg(priv, SECONDARY_STREAM_DISABLE, + group_offset, PHYTIUM_DP_SECONDARY_STREAM_ENABLE); +} + +static void phytium_dp_hw_audio_digital_mute(struct phytium_dp_device *phytium_dp, bool enable) +{ + struct phytium_display_private *priv = phytium_dp->dev->dev_private; + int port = phytium_dp->port; + uint32_t group_offset = priv->dp_reg_base[port]; + + if (enable) + phytium_writel_reg(priv, CHANNEL_MUTE_ENABLE, + group_offset, PHYTIUM_DP_SEC_AUDIO_ENABLE); + else + phytium_writel_reg(priv, SEC_AUDIO_ENABLE, + group_offset, PHYTIUM_DP_SEC_AUDIO_ENABLE); +} + +static int +phytium_dp_hw_audio_hw_params(struct phytium_dp_device *phytium_dp, struct audio_info audio_info) +{ + struct phytium_display_private *priv = phytium_dp->dev->dev_private; + int port = phytium_dp->port; + int ret = 0, data_window = 0; + const struct dp_audio_n_m *n_m = NULL; + uint32_t fs, ws, fs_accurac; + uint32_t group_offset = priv->dp_reg_base[port]; + + DRM_DEBUG_KMS("%s:set port%d sample_rate(%d) channels(%d) sample_width(%d)\n", + __func__, phytium_dp->port, audio_info.sample_rate, + audio_info.channels, audio_info.sample_width); + + phytium_writel_reg(priv, INPUT_SELECT_I2S, group_offset, PHYTIUM_DP_SEC_INPUT_SELECT); + phytium_writel_reg(priv, APB_CLOCK/audio_info.sample_rate, + group_offset, PHYTIUM_DP_SEC_DIRECT_CLKDIV); + phytium_writel_reg(priv, audio_info.channels & CHANNEL_MASK, + group_offset, PHYTIUM_DP_SEC_CHANNEL_COUNT); + phytium_writel_reg(priv, CHANNEL_MAP_DEFAULT, group_offset, PHYTIUM_DP_SEC_CHANNEL_MAP); + data_window = 90*(phytium_dp->link_rate)/100 + *(phytium_dp->mode.htotal - phytium_dp->mode.hdisplay) + /phytium_dp->mode.clock/4; + phytium_writel_reg(priv, data_window, group_offset, PHYTIUM_DP_SEC_DATA_WINDOW); + phytium_writel_reg(priv, 0xb5, group_offset, PHYTIUM_DP_SEC_CS_CATEGORY_CODE); + + phytium_writel_reg(priv, CLOCK_MODE_SYNC, group_offset, PHYTIUM_DP_SEC_CLOCK_MODE); + phytium_writel_reg(priv, CS_SOURCE_FORMAT_DEFAULT, + group_offset, PHYTIUM_DP_SEC_CS_SOURCE_FORMAT); + + switch (audio_info.sample_rate) { + case 32000: + fs = ORIG_FREQ_32000; + fs_accurac = SAMPLING_FREQ_32000; + break; + case 44100: + fs = ORIG_FREQ_44100; + fs_accurac = SAMPLING_FREQ_44100; + break; + case 48000: + fs = ORIG_FREQ_48000; + fs_accurac = SAMPLING_FREQ_48000; + break; + case 96000: + fs = ORIG_FREQ_96000; + fs_accurac = SAMPLING_FREQ_96000; + break; + case 176400: + fs = ORIG_FREQ_176400; + fs_accurac = SAMPLING_FREQ_176400; + break; + case 192000: + fs = ORIG_FREQ_192000; + fs_accurac = SAMPLING_FREQ_192000; + break; + default: + DRM_ERROR("dp not support sample_rate %d\n", audio_info.sample_rate); + goto out; + } + + switch (audio_info.sample_width) { + case 16: + ws = WORD_LENGTH_16; + break; + case 18: + ws = WORD_LENGTH_18; + break; + case 20: + ws = WORD_LENGTH_20; + break; + case 24: + ws = WORD_LENGTH_24; + break; + default: + DRM_ERROR("dp not support sample_width %d\n", audio_info.sample_width); + goto out; + } + + phytium_writel_reg(priv, ((fs&ORIG_FREQ_MASK)<link_rate, audio_info.sample_rate); + if (n_m == NULL) { + DRM_NOTE("can not get n_m for link_rate(%d) and sample_rate(%d)\n", + phytium_dp->link_rate, audio_info.sample_rate); + phytium_writel_reg(priv, 0, group_offset, PHYTIUM_DP_SEC_MAUD); + phytium_writel_reg(priv, 0, group_offset, PHYTIUM_DP_SEC_NAUD); + + } else { + phytium_writel_reg(priv, n_m->m, group_offset, PHYTIUM_DP_SEC_MAUD); + phytium_writel_reg(priv, n_m->n, group_offset, PHYTIUM_DP_SEC_NAUD); + } + phytium_writel_reg(priv, SECONDARY_STREAM_ENABLE, + group_offset, PHYTIUM_DP_SECONDARY_STREAM_ENABLE); + phytium_dp->audio_info = audio_info; + + return 0; + +out: + phytium_writel_reg(priv, SECONDARY_STREAM_DISABLE, + group_offset, PHYTIUM_DP_SECONDARY_STREAM_ENABLE); + + return ret; +} + +void phytium_dp_hw_disable_video(struct phytium_dp_device *phytium_dp) +{ + struct drm_device *dev = phytium_dp->dev; + struct phytium_display_private *priv = dev->dev_private; + int port = phytium_dp->port; + uint32_t group_offset = priv->dp_reg_base[port]; + + phytium_writel_reg(priv, SST_MST_SOURCE_0_DISABLE, + group_offset, PHYTIUM_DP_VIDEO_STREAM_ENABLE); +} + +bool phytium_dp_hw_video_is_enable(struct phytium_dp_device *phytium_dp) +{ + struct drm_device *dev = phytium_dp->dev; + struct phytium_display_private *priv = dev->dev_private; + int port = phytium_dp->port, config; + uint32_t group_offset = priv->dp_reg_base[port]; + + config = phytium_readl_reg(priv, group_offset, PHYTIUM_DP_VIDEO_STREAM_ENABLE); + return config ? true : false; +} + +void phytium_dp_hw_enable_video(struct phytium_dp_device *phytium_dp) +{ + struct drm_device *dev = phytium_dp->dev; + struct phytium_display_private *priv = dev->dev_private; + int port = phytium_dp->port; + uint32_t group_offset = priv->dp_reg_base[port]; + + phytium_writel_reg(priv, SST_MST_SOURCE_0_ENABLE, + group_offset, PHYTIUM_DP_VIDEO_STREAM_ENABLE); + phytium_writel_reg(priv, LINK_SOFT_RESET, group_offset, PHYTIUM_DP_SOFT_RESET); +} + +void phytium_dp_hw_config_video(struct phytium_dp_device *phytium_dp) +{ + struct drm_device *dev = phytium_dp->dev; + struct phytium_display_private *priv = dev->dev_private; + int port = phytium_dp->port; + uint32_t group_offset = priv->dp_reg_base[port]; + unsigned long link_bw, date_rate = 0; + struct drm_display_info *display_info = &phytium_dp->connector.display_info; + unsigned char tu_size = 64; + unsigned long data_per_tu = 0; + int symbols_per_tu, frac_symbols_per_tu, symbol_count, udc, value; + + /* cal M/N and tu_size */ + phytium_writel_reg(priv, phytium_dp->mode.crtc_clock/10, group_offset, PHYTIUM_DP_M_VID); + phytium_writel_reg(priv, phytium_dp->link_rate/10, group_offset, PHYTIUM_DP_N_VID); + link_bw = phytium_dp->link_rate * phytium_dp->link_lane_count; + date_rate = (phytium_dp->mode.crtc_clock * display_info->bpc * 3)/8; + + /* mul 10 for register setting */ + data_per_tu = 10*tu_size * date_rate/link_bw; + symbols_per_tu = (data_per_tu/10)&0xff; + if (symbols_per_tu == 63) + frac_symbols_per_tu = 0; + else + frac_symbols_per_tu = (data_per_tu%10*16/10) & 0xf; + phytium_writel_reg(priv, frac_symbols_per_tu<<24 | symbols_per_tu<<16 | tu_size, + group_offset, PHYTIUM_DP_TRANSFER_UNIT_SIZE); + + symbol_count = (phytium_dp->mode.crtc_hdisplay*display_info->bpc*3 + 7)/8; + udc = (symbol_count + phytium_dp->link_lane_count - 1)/phytium_dp->link_lane_count; + phytium_writel_reg(priv, udc, group_offset, PHYTIUM_DP_DATA_COUNT); + + /* config main stream attributes */ + phytium_writel_reg(priv, phytium_dp->mode.crtc_htotal, + group_offset, PHYTIUM_DP_MAIN_LINK_HTOTAL); + phytium_writel_reg(priv, phytium_dp->mode.crtc_hdisplay, + group_offset, PHYTIUM_DP_MAIN_LINK_HRES); + phytium_writel_reg(priv, + phytium_dp->mode.crtc_hsync_end - phytium_dp->mode.crtc_hsync_start, + group_offset, PHYTIUM_DP_MAIN_LINK_HSWIDTH); + phytium_writel_reg(priv, phytium_dp->mode.crtc_htotal - phytium_dp->mode.crtc_hsync_start, + group_offset, PHYTIUM_DP_MAIN_LINK_HSTART); + phytium_writel_reg(priv, phytium_dp->mode.crtc_vtotal, + group_offset, PHYTIUM_DP_MAIN_LINK_VTOTAL); + phytium_writel_reg(priv, phytium_dp->mode.crtc_vdisplay, + group_offset, PHYTIUM_DP_MAIN_LINK_VRES); + phytium_writel_reg(priv, + phytium_dp->mode.crtc_vsync_end - phytium_dp->mode.crtc_vsync_start, + group_offset, PHYTIUM_DP_MAIN_LINK_VSWIDTH); + phytium_writel_reg(priv, phytium_dp->mode.crtc_vtotal - phytium_dp->mode.crtc_vsync_start, + group_offset, PHYTIUM_DP_MAIN_LINK_VSTART); + + value = 0; + if (phytium_dp->mode.flags & DRM_MODE_FLAG_PHSYNC) + value = value & (~HSYNC_POLARITY_LOW); + else + value = value | HSYNC_POLARITY_LOW; + + if (phytium_dp->mode.flags & DRM_MODE_FLAG_PVSYNC) + value = value & (~VSYNC_POLARITY_LOW); + else + value = value | VSYNC_POLARITY_LOW; + phytium_writel_reg(priv, value, group_offset, PHYTIUM_DP_MAIN_LINK_POLARITY); + + switch (display_info->bpc) { + case 10: + value = (MISC0_BIT_DEPTH_10BIT << MISC0_BIT_DEPTH_OFFSET); + break; + case 6: + value = (MISC0_BIT_DEPTH_6BIT << MISC0_BIT_DEPTH_OFFSET); + break; + default: + value = (MISC0_BIT_DEPTH_8BIT << MISC0_BIT_DEPTH_OFFSET); + break; + } + value |= (MISC0_COMPONENT_FORMAT_RGB << MISC0_COMPONENT_FORMAT_SHIFT) + | MISC0_SYNCHRONOUS_CLOCK; + phytium_writel_reg(priv, value, group_offset, PHYTIUM_DP_MAIN_LINK_MISC0); + phytium_writel_reg(priv, 0, group_offset, PHYTIUM_DP_MAIN_LINK_MISC1); + + value = USER_ODDEVEN_POLARITY_HIGH | USER_DATA_ENABLE_POLARITY_HIGH; + if (phytium_dp->mode.flags & DRM_MODE_FLAG_PHSYNC) + value = value | USER_HSYNC_POLARITY_HIGH; + else + value = value & (~USER_HSYNC_POLARITY_HIGH); + if (phytium_dp->mode.flags & DRM_MODE_FLAG_PVSYNC) + value = value | USER_VSYNC_POLARITY_HIGH; + else + value = value & (~USER_VSYNC_POLARITY_HIGH); + phytium_writel_reg(priv, value, group_offset, PHYTIUM_DP_USER_SYNC_POLARITY); +} + +void phytium_dp_hw_disable_output(struct phytium_dp_device *phytium_dp) +{ + struct drm_device *dev = phytium_dp->dev; + struct phytium_display_private *priv = dev->dev_private; + int port = phytium_dp->port; + uint32_t group_offset = priv->dp_reg_base[port]; + + phytium_writel_reg(priv, TRANSMITTER_OUTPUT_DISABLE, + group_offset, PHYTIUM_DP_TRANSMITTER_OUTPUT_ENABLE); + phytium_writel_reg(priv, LINK_SOFT_RESET, group_offset, PHYTIUM_DP_SOFT_RESET); +} + +void phytium_dp_hw_enable_output(struct phytium_dp_device *phytium_dp) +{ + struct drm_device *dev = phytium_dp->dev; + struct phytium_display_private *priv = dev->dev_private; + int port = phytium_dp->port; + uint32_t group_offset = priv->dp_reg_base[port]; + + phytium_writel_reg(priv, LINK_SOFT_RESET, group_offset, PHYTIUM_DP_SOFT_RESET); + phytium_writel_reg(priv, TRANSMITTER_OUTPUT_ENABLE, + group_offset, PHYTIUM_DP_TRANSMITTER_OUTPUT_ENABLE); +} + +void phytium_dp_hw_enable_input_source(struct phytium_dp_device *phytium_dp) +{ + struct drm_device *dev = phytium_dp->dev; + struct phytium_display_private *priv = dev->dev_private; + int port = phytium_dp->port; + uint32_t group_offset = priv->dp_reg_base[port]; + + phytium_writel_reg(priv, VIRTUAL_SOURCE_0_ENABLE, + group_offset, PHYTIUM_INPUT_SOURCE_ENABLE); +} + +void phytium_dp_hw_disable_input_source(struct phytium_dp_device *phytium_dp) +{ + struct drm_device *dev = phytium_dp->dev; + struct phytium_display_private *priv = dev->dev_private; + int port = phytium_dp->port; + + phytium_writel_reg(priv, (~VIRTUAL_SOURCE_0_ENABLE)&VIRTUAL_SOURCE_0_ENABLE_MASK, + priv->dp_reg_base[port], PHYTIUM_INPUT_SOURCE_ENABLE); +} + +bool phytium_dp_hw_output_is_enable(struct phytium_dp_device *phytium_dp) +{ + struct drm_device *dev = phytium_dp->dev; + struct phytium_display_private *priv = dev->dev_private; + int port = phytium_dp->port; + uint32_t group_offset = priv->dp_reg_base[port]; + int config = 0; + + config = phytium_readl_reg(priv, group_offset, PHYTIUM_DP_TRANSMITTER_OUTPUT_ENABLE); + return config ? true : false; +} + +static void phytium_dp_hw_get_hpd_state(struct phytium_dp_device *phytium_dp) +{ + struct drm_device *dev = phytium_dp->dev; + struct phytium_display_private *priv = dev->dev_private; + int port = phytium_dp->port; + uint32_t val = 0, raw_state = 0; + uint32_t group_offset = priv->dp_reg_base[port]; + + val = phytium_readl_reg(priv, group_offset, PHYTIUM_DP_INTERRUPT_RAW_STATUS); + + /* maybe miss hpd, so used for clear PHYTIUM_DP_INTERRUPT_RAW_STATUS */ + phytium_readl_reg(priv, group_offset, PHYTIUM_DP_INTERRUPT_STATUS); + raw_state = phytium_readl_reg(priv, group_offset, PHYTIUM_DP_SINK_HPD_STATE); + if (val & HPD_EVENT) + phytium_dp->dp_hpd_state.hpd_event_state = true; + + if (val & HPD_IRQ) + phytium_dp->dp_hpd_state.hpd_irq_state = true; + + if (raw_state & HPD_CONNECT) + phytium_dp->dp_hpd_state.hpd_raw_state = true; + else + phytium_dp->dp_hpd_state.hpd_raw_state = false; +} + +void phytium_dp_hw_hpd_irq_setup(struct phytium_dp_device *phytium_dp, bool enable) +{ + struct drm_device *dev = phytium_dp->dev; + struct phytium_display_private *priv = dev->dev_private; + int port = phytium_dp->port; + uint32_t group_offset = priv->dp_reg_base[port]; + + phytium_dp->dp_hpd_state.hpd_irq_enable = enable; + if (enable) + phytium_writel_reg(priv, HPD_OTHER_MASK, group_offset, PHYTIUM_DP_INTERRUPT_MASK); + else + phytium_writel_reg(priv, HPD_IRQ_MASK|HPD_EVENT_MASK|HPD_OTHER_MASK, + group_offset, PHYTIUM_DP_INTERRUPT_MASK); +} + +int phytium_dp_hw_init(struct phytium_dp_device *phytium_dp) +{ + int ret = 0; + uint8_t count = 0; + + phytium_dp->source_rates = phytium_rate; + phytium_dp->num_source_rates = num_source_rates; + count = phytium_dp->funcs->dp_hw_get_source_lane_count(phytium_dp); + phytium_dp->source_max_lane_count = count; + + ret = phytium_dp->funcs->dp_hw_reset(phytium_dp); + if (ret) + goto out; + ret = phytium_dp->funcs->dp_hw_init_phy(phytium_dp); + if (ret) + goto out; + + phytium_dp->fast_train_support = false; + phytium_dp->hw_spread_enable = phytium_dp->funcs->dp_hw_spread_is_enable(phytium_dp); + +out: + return ret; +} + +static int phytium_dp_dpcd_get_tp_link(struct phytium_dp_device *phytium_dp, + uint8_t *test_lane_count, + uint32_t *test_link_rate) +{ + uint8_t test_link_bw; + int ret; + + ret = drm_dp_dpcd_readb(&phytium_dp->aux, DP_TEST_LANE_COUNT, + test_lane_count); + if (ret <= 0) { + DRM_DEBUG_KMS("test pattern Lane count read failed(%d)\n", ret); + goto failed; + } + + ret = drm_dp_dpcd_readb(&phytium_dp->aux, DP_TEST_LINK_RATE, + &test_link_bw); + if (ret <= 0) { + DRM_DEBUG_KMS("test pattern link rate read failed(%d)\n", ret); + goto failed; + } + *test_link_rate = drm_dp_bw_code_to_link_rate(test_link_bw); + + return 0; +failed: + return ret; +} + +static int phytium_dp_dpcd_set_link(struct phytium_dp_device *phytium_dp, + uint8_t lane_count, uint32_t link_rate) +{ + uint8_t link_config[2]; + int ret = 0; + + link_config[0] = drm_dp_link_rate_to_bw_code(link_rate); + link_config[1] = lane_count; + if (drm_dp_enhanced_frame_cap(phytium_dp->dpcd)) { + link_config[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN; + } + ret = drm_dp_dpcd_write(&phytium_dp->aux, DP_LINK_BW_SET, link_config, 2); + if (ret < 0) { + DRM_NOTE("write dpcd DP_LINK_BW_SET fail: ret:%d\n", ret); + goto failed; + } + + if (phytium_dp->hw_spread_enable) + link_config[0] = DP_SPREAD_AMP_0_5; + else + link_config[0] = 0; + link_config[1] = DP_SET_ANSI_8B10B; + ret = drm_dp_dpcd_write(&phytium_dp->aux, DP_DOWNSPREAD_CTRL, link_config, 2); + if (ret < 0) { + DRM_ERROR("write DP_DOWNSPREAD_CTRL fail: ret:%d\n", ret); + goto failed; + } + + return 0; +failed: + return ret; +} + +static int phytium_dp_dpcd_set_test_pattern(struct phytium_dp_device *phytium_dp, + uint8_t test_pattern) +{ + unsigned char value; + int ret; + + if (phytium_dp_coding_8b10b_need_enable(test_pattern)) + value = DP_SET_ANSI_8B10B; + else + value = 0; + ret = drm_dp_dpcd_writeb(&phytium_dp->aux, DP_MAIN_LINK_CHANNEL_CODING_SET, value); + if (ret < 0) { + DRM_ERROR("write DP_MAIN_LINK_CHANNEL_CODING_SET fail: ret:%d\n", ret); + goto failed; + } + + if (phytium_dp_scrambled_need_enable(test_pattern)) + value = DP_TRAINING_PATTERN_DISABLE; + else + value = (DP_TRAINING_PATTERN_DISABLE | DP_LINK_SCRAMBLING_DISABLE); + + ret = drm_dp_dpcd_writeb(&phytium_dp->aux, DP_TRAINING_PATTERN_SET, value); + if (ret < 0) { + DRM_ERROR("write DP_TRAINING_PATTERN_SET fail: ret:%d\n", ret); + goto failed; + } + + ret = drm_dp_dpcd_writeb(&phytium_dp->aux, DP_LINK_QUAL_LANE0_SET, test_pattern); + if (ret < 0) { + DRM_ERROR("write DP_TRAINING_PATTERN_SET fail: ret:%d\n", ret); + goto failed; + } + + return 0; +failed: + return ret; +} + +static int phytium_dp_dpcd_set_train_pattern(struct phytium_dp_device *phytium_dp, + uint8_t train_pattern) +{ + uint8_t value; + int ret; + + /* Scrambling is disabled for TPS1/2/3 and enabled for TPS4 */ + if (train_pattern == DP_TRAINING_PATTERN_4 || train_pattern == DP_TRAINING_PATTERN_DISABLE) + value = train_pattern; + else + value = (train_pattern | DP_LINK_SCRAMBLING_DISABLE); + + ret = drm_dp_dpcd_writeb(&phytium_dp->aux, DP_TRAINING_PATTERN_SET, value); + if (ret < 0) { + DRM_NOTE("write DP_TRAINING_PATTERN_SET fail: ret:%d\n", ret); + goto failed; + } + + return 0; +failed: + return ret; +} + +static int +phytium_dp_dpcd_set_lane_setting(struct phytium_dp_device *phytium_dp, uint8_t *train_set) +{ + int ret = 0; + + ret = drm_dp_dpcd_write(&phytium_dp->aux, DP_TRAINING_LANE0_SET, + phytium_dp->train_set, 4); + if (ret < 0) { + DRM_ERROR("write DP_TRAINING_LANE0_SET fail: ret:%d\n", ret); + return ret; + } + + return 0; +} + +static int +phytium_dp_dpcd_get_adjust_request(struct phytium_dp_device *phytium_dp, uint8_t lane_count) +{ + int ret = 0; + uint8_t link_status[DP_LINK_STATUS_SIZE]; + + ret = drm_dp_dpcd_read(&phytium_dp->aux, DP_LANE0_1_STATUS, + link_status, DP_LINK_STATUS_SIZE); + if (ret < 0) { + DRM_ERROR("failed to get link status(DP_LANE0_1_STATUS)\n"); + goto failed; + } + phytium_get_adjust_train(phytium_dp, link_status, lane_count); + + return 0; +failed: + return ret; +} + +void phytium_dp_dpcd_sink_dpms(struct phytium_dp_device *phytium_dp, int mode) +{ + int ret, i; + + if (phytium_dp->dpcd[DP_DPCD_REV] < 0x11) + return; + if (mode != DRM_MODE_DPMS_ON) { + ret = drm_dp_dpcd_writeb(&phytium_dp->aux, DP_SET_POWER, DP_SET_POWER_D3); + } else { + for (i = 0; i < 3; i++) { + ret = drm_dp_dpcd_writeb(&phytium_dp->aux, DP_SET_POWER, DP_SET_POWER_D0); + if (ret == 1) + break; + msleep(20); + } + } + + if (ret != 1) + DRM_DEBUG_KMS("failed to %s sink power state\n", + mode == DRM_MODE_DPMS_ON ? "enable" : "disable"); +} + +static bool phytium_dp_link_training_clock_recovery(struct phytium_dp_device *phytium_dp) +{ + int ret; + unsigned char voltage, max_vswing_tries; + int voltage_tries; + + /* clear the test pattern */ + phytium_dp_hw_set_test_pattern(phytium_dp, phytium_dp->link_lane_count, + PHYTIUM_PHY_TP_NONE, NULL, 0); + + /* config source and sink's link rate and lane count */ + phytium_dp_hw_set_link(phytium_dp, phytium_dp->link_lane_count, phytium_dp->link_rate); + ret = phytium_dp_dpcd_set_link(phytium_dp, phytium_dp->link_lane_count, + phytium_dp->link_rate); + if (ret < 0) { + DRM_NOTE("phytium_dp_dpcd_set_link failed(ret=%d)\n", ret); + return false; + } + + /* config source's voltage swing and pre-emphasis(103-106) */ + memset(phytium_dp->train_set, 0, sizeof(phytium_dp->train_set)); + phytium_dp_hw_set_lane_setting(phytium_dp, phytium_dp->link_rate, + phytium_dp->train_set[0]); + + /* config train pattern */ + phytium_dp_hw_set_train_pattern(phytium_dp, DP_TRAINING_PATTERN_1); + ret = phytium_dp_dpcd_set_train_pattern(phytium_dp, DP_TRAINING_PATTERN_1); + if (ret < 0) { + DRM_ERROR("phytium_dp_dpcd_set_train_pattern fail: ret:%d\n", ret); + return false; + } + + /* config sink's voltage swing and pre-emphasis(103-106) */ + ret = phytium_dp_dpcd_set_lane_setting(phytium_dp, phytium_dp->train_set); + if (ret < 0) { + DRM_ERROR("phytium_dp_dpcd_set_lane_setting fail: ret:%d\n", ret); + return false; + } + + voltage_tries = 1; + max_vswing_tries = 0; + for (;;) { + unsigned char link_status[DP_LINK_STATUS_SIZE]; + drm_dp_link_train_clock_recovery_delay(phytium_dp->dpcd); + /* get link status 0x202-0x207 */ + ret = drm_dp_dpcd_read(&phytium_dp->aux, DP_LANE0_1_STATUS, + link_status, DP_LINK_STATUS_SIZE); + if (ret < 0) { + DRM_ERROR("failed to get link status(DP_LANE0_1_STATUS)\n"); + return false; + } + + if (drm_dp_clock_recovery_ok(link_status, phytium_dp->link_lane_count)) { + DRM_DEBUG_KMS("clock revorery ok\n"); + return true; + } + + if (voltage_tries == 5) { + DRM_DEBUG_KMS("Same voltage tried 5 times\n"); + return false; + } + + if (max_vswing_tries == 1) { + DRM_DEBUG_KMS("Max Voltage Swing reached\n"); + return false; + } + + voltage = phytium_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK; + + /* config source and sink's voltage swing and pre-emphasis(103-106) */ + phytium_get_adjust_train(phytium_dp, link_status, phytium_dp->link_lane_count); + phytium_dp_hw_set_lane_setting(phytium_dp, phytium_dp->link_rate, + phytium_dp->train_set[0]); + ret = phytium_dp_dpcd_set_lane_setting(phytium_dp, phytium_dp->train_set); + if (ret < 0) { + DRM_ERROR("phytium_dp_dpcd_set_lane_setting fail: ret:%d\n", ret); + return false; + } + + if ((phytium_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) == voltage) + ++voltage_tries; + else + voltage_tries = 1; + + if (phytium_dp->train_set[0] & DP_TRAIN_MAX_SWING_REACHED) + ++max_vswing_tries; + + DRM_DEBUG_KMS("try train_set:0x%x voltage_tries:%d max_vswing_tries:%d\n", + phytium_dp->train_set[0], voltage_tries, max_vswing_tries); + } +} + +static unsigned int phytium_dp_get_training_pattern(struct phytium_dp_device *phytium_dp) +{ + bool sink_tps3, sink_tps4; + + sink_tps4 = drm_dp_tps4_supported(phytium_dp->dpcd); + if (sink_tps4) + return DP_TRAINING_PATTERN_4; + else if (phytium_dp->link_rate == 810000) + DRM_DEBUG_KMS("8.1 Gbps link rate without sink TPS4 support\n"); + + sink_tps3 = drm_dp_tps3_supported(phytium_dp->dpcd); + if (sink_tps3) + return DP_TRAINING_PATTERN_3; + else if (phytium_dp->link_rate >= 540000) + DRM_DEBUG_KMS(">=5.4/6.48 Gbps link rate without sink TPS3 support\n"); + + return DP_TRAINING_PATTERN_2; +} + +static bool phytium_dp_link_training_channel_equalization(struct phytium_dp_device *phytium_dp) +{ + unsigned int training_pattern; + int tries, ret; + unsigned char link_status[DP_LINK_STATUS_SIZE]; + bool channel_eq = false; + + /* config source and sink's voltage swing and pre-emphasis(103-106), from clock recovery */ + phytium_dp_hw_set_lane_setting(phytium_dp, phytium_dp->link_rate, + phytium_dp->train_set[0]); + ret = phytium_dp_dpcd_set_lane_setting(phytium_dp, phytium_dp->train_set); + if (ret < 0) { + DRM_ERROR("phytium_dp_dpcd_set_lane_setting fail: ret:%d\n", ret); + return channel_eq; + } + + /* config source and sink's train_pattern x */ + training_pattern = phytium_dp_get_training_pattern(phytium_dp); + phytium_dp_hw_set_train_pattern(phytium_dp, training_pattern); + ret = phytium_dp_dpcd_set_train_pattern(phytium_dp, training_pattern); + if (ret < 0) { + DRM_ERROR("phytium_dp_dpcd_set_train_pattern fail: ret:%d\n", ret); + return channel_eq; + } + + for (tries = 0; tries < 5; tries++) { + drm_dp_link_train_channel_eq_delay(phytium_dp->dpcd); + + /* get link status 0x202-0x207 */ + ret = drm_dp_dpcd_read(&phytium_dp->aux, DP_LANE0_1_STATUS, + link_status, DP_LINK_STATUS_SIZE); + if (ret < 0) { + DRM_ERROR("failed to get link status(DP_LANE0_1_STATUS)\n"); + break; + } + + /* Make sure clock is still ok */ + if (!drm_dp_clock_recovery_ok(link_status, phytium_dp->link_lane_count)) { + DRM_DEBUG_KMS("CR check failed, cannot continue channel equalization\n"); + break; + } + + if (drm_dp_channel_eq_ok(link_status, phytium_dp->link_lane_count)) { + channel_eq = true; + DRM_DEBUG_KMS("Channel EQ done. DP Training successful\n"); + break; + } + + /* config source and sink's voltage swing and pre-emphasis(103-106) */ + phytium_get_adjust_train(phytium_dp, link_status, phytium_dp->link_lane_count); + phytium_dp_hw_set_lane_setting(phytium_dp, phytium_dp->link_rate, + phytium_dp->train_set[0]); + ret = phytium_dp_dpcd_set_lane_setting(phytium_dp, phytium_dp->train_set); + if (ret < 0) { + DRM_ERROR("phytium_dp_dpcd_set_lane_setting fail: ret:%d\n", ret); + break; + } + } + + /* Try 5 times, else fail and try at lower BW */ + if (tries == 5) + DRM_DEBUG_KMS("Channel equalization failed 5 times\n"); + + return channel_eq; +} + +static void phytium_dp_train_retry_work_fn(struct work_struct *work) +{ + struct phytium_dp_device *phytium_dp = train_retry_to_dp_device(work); + struct drm_connector *connector; + + connector = &phytium_dp->connector; + DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", connector->base.id, connector->name); + mutex_lock(&connector->dev->mode_config.mutex); + drm_connector_set_link_status_property(connector, DRM_MODE_LINK_STATUS_BAD); + mutex_unlock(&connector->dev->mode_config.mutex); + drm_kms_helper_hotplug_event(connector->dev); +} + +/* return index of rate in rates array, or -1 if not found */ +static int phytium_dp_rate_index(const int *rates, int len, int rate) +{ + int i; + + for (i = 0; i < len; i++) + if (rate == rates[i]) + return i; + + return -1; +} + +int phytium_dp_get_link_train_fallback_values(struct phytium_dp_device *phytium_dp) +{ + int index, ret = 0; + + if (phytium_dp->is_edp) { + phytium_dp->train_retry_count++; + DRM_INFO("Retrying Link training for eDP(%d) with same parameters\n", + phytium_dp->port); + goto out; + } else { + index = phytium_dp_rate_index(phytium_dp->common_rates, + phytium_dp->num_common_rates, + phytium_dp->link_rate); + if (index > 0) { + phytium_dp->link_rate = phytium_dp->common_rates[index - 1]; + } else if (phytium_dp->link_lane_count > 1) { + phytium_dp->link_rate = phytium_dp->max_link_rate; + phytium_dp->link_lane_count = phytium_dp->link_lane_count >> 1; + } else { + phytium_dp->train_retry_count++; + phytium_dp->link_rate = phytium_dp->max_link_rate; + phytium_dp->link_lane_count = phytium_dp->max_link_lane_count; + DRM_INFO("Retrying Link training for DP(%d) with maximal parameters\n", + phytium_dp->port); + ret = -1; + } + } + +out: + return ret; +} + +static int +phytium_dp_stop_link_train(struct phytium_dp_device *phytium_dp) +{ + int ret; + + /* config source and sink's train_pattern x: DP_TRAINING_PATTERN_DISABLE */ + phytium_dp_hw_set_train_pattern(phytium_dp, DP_TRAINING_PATTERN_DISABLE); + + ret = phytium_dp_dpcd_set_train_pattern(phytium_dp, DP_TRAINING_PATTERN_DISABLE); + if (ret < 0) { + DRM_NOTE("phytium_dp_dpcd_set_train_pattern fail: ret:%d\n", ret); + return ret; + } + + return 0; +} + +int phytium_dp_start_link_train(struct phytium_dp_device *phytium_dp) +{ + int ret = 0; + + phytium_dp_hw_disable_output(phytium_dp); + phytium_dp_hw_disable_input_source(phytium_dp); + phytium_dp_hw_disable_video(phytium_dp); + phytium_dp_hw_enable_input_source(phytium_dp); + phytium_dp_hw_enable_output(phytium_dp); + phytium_dp_dpcd_sink_dpms(phytium_dp, DRM_MODE_DPMS_OFF); + phytium_dp_dpcd_sink_dpms(phytium_dp, DRM_MODE_DPMS_ON); + + if (!phytium_dp_link_training_clock_recovery(phytium_dp)) + goto failure_handling; + + if (!phytium_dp_link_training_channel_equalization(phytium_dp)) + goto failure_handling; + + ret = phytium_dp_stop_link_train(phytium_dp); + if (ret < 0) { + DRM_NOTE("phytium_dp_stop_link_train failed: ret = %d\n", ret); + goto out; + } + + if (phytium_dp->trigger_train_fail) { + phytium_dp->trigger_train_fail--; + goto failure_handling; + } + phytium_dp->train_retry_count = 0; + + DRM_DEBUG_KMS("[CONNECTOR:%d:%s] Link Training Pass at Link Rate = %d, Lane count = %d\n", + phytium_dp->connector.base.id, + phytium_dp->connector.name, phytium_dp->link_rate, + phytium_dp->link_lane_count); + + return 0; + +failure_handling: + DRM_INFO("[CONNECTOR:%d:%s] Link Training failed at Link Rate = %d, Lane count = %d", + phytium_dp->connector.base.id, + phytium_dp->connector.name, + phytium_dp->link_rate, phytium_dp->link_lane_count); + + ret = phytium_dp_stop_link_train(phytium_dp); + if (ret < 0) { + DRM_NOTE("phytium_dp_stop_link_train failed: ret = %d\n", ret); + goto out; + } + + phytium_dp_get_link_train_fallback_values(phytium_dp); + if (phytium_dp->train_retry_count < 5) + schedule_work(&phytium_dp->train_retry_work); + else + DRM_ERROR("DP(%d) Link Training Unsuccessful, and stop Training\n", + phytium_dp->port); + +out: + return -1; +} + +static bool phytium_dp_needs_link_retrain(struct phytium_dp_device *phytium_dp) +{ + unsigned char link_status[DP_LINK_STATUS_SIZE]; + int ret = 0; + + /* get link status 0x202-0x207 */ + ret = drm_dp_dpcd_read(&phytium_dp->aux, DP_LANE0_1_STATUS, + link_status, DP_LINK_STATUS_SIZE); + if (ret < 0) { + DRM_ERROR("failed to get link status(DP_LANE0_1_STATUS)\n"); + return true; + } + + if ((phytium_dp->link_rate == 0) || (phytium_dp->link_lane_count == 0)) { + DRM_DEBUG_KMS("link_rate(%d) or lane_count(%d) is invalid\n", + phytium_dp->link_rate, phytium_dp->link_lane_count); + return true; + } + + /* Make sure clock is still ok */ + if (!drm_dp_clock_recovery_ok(link_status, phytium_dp->link_lane_count)) { + DRM_DEBUG_KMS("Clock recovery check failed\n"); + return true; + } + + if (!drm_dp_channel_eq_ok(link_status, phytium_dp->link_lane_count)) { + DRM_DEBUG_KMS("Channel EQ check failed\n"); + return true; + } + + if (!phytium_dp_hw_output_is_enable(phytium_dp)) { + DRM_DEBUG_KMS("check DP output enable failed\n"); + return true; + } + return false; +} + +static bool +phytium_dp_get_sink_irq(struct phytium_dp_device *phytium_dp, u8 *sink_irq_vector) +{ + return drm_dp_dpcd_readb(&phytium_dp->aux, DP_DEVICE_SERVICE_IRQ_VECTOR, + sink_irq_vector) == 1; +} + +static uint8_t phytium_dp_autotest_phy_pattern(struct phytium_dp_device *phytium_dp) +{ + union phytium_phy_tp phytium_phy_tp; + int ret; + unsigned char test_80_bit_pattern[ + (DP_TEST_80BIT_CUSTOM_PATTERN_79_72 - + DP_TEST_80BIT_CUSTOM_PATTERN_7_0)+1] = {0}; + unsigned char test_pattern; + unsigned int offset; + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)) + offset = DP_TEST_PHY_PATTERN; +#else + offset = DP_PHY_TEST_PATTERN; +#endif + + ret = drm_dp_dpcd_read(&phytium_dp->aux, offset, + &phytium_phy_tp.raw, + sizeof(phytium_phy_tp)); + if (ret <= 0) { + DRM_DEBUG_KMS("Could not read DP_TEST_PHY_PATTERN\n"); + goto failed; + } + + test_pattern = phytium_phy_tp.bits.PATTERN; + + if (test_pattern == PHYTIUM_PHY_TP_80BIT_CUSTOM) { + ret = drm_dp_dpcd_read(&phytium_dp->aux, DP_TEST_80BIT_CUSTOM_PATTERN_7_0, + test_80_bit_pattern, + sizeof(test_80_bit_pattern)); + if (ret <= 0) { + DRM_DEBUG_KMS("Could not read DP_TEST_PHY_PATTERN\n"); + goto failed; + } + } + + /* config source and sink's link rate and link count */ + ret = phytium_dp_dpcd_get_tp_link(phytium_dp, &phytium_dp->compliance.test_lane_count, + &phytium_dp->compliance.test_link_rate); + if (ret < 0) { + DRM_ERROR("phytium_dp_dpcd_get_tp_link fail: ret:%d\n", ret); + goto failed; + } + + phytium_dp_hw_set_link(phytium_dp, phytium_dp->compliance.test_lane_count, + phytium_dp->compliance.test_link_rate); + ret = phytium_dp_dpcd_set_link(phytium_dp, phytium_dp->compliance.test_lane_count, + phytium_dp->compliance.test_link_rate); + if (ret < 0) { + DRM_ERROR("phytium_dp_dpcd_set_link fail: ret:%d\n", ret); + goto failed_dpcd_set_link; + } + + /* config source and sink's lane setting: voltage swing and pre-emphasis */ + ret = phytium_dp_dpcd_get_adjust_request(phytium_dp, + phytium_dp->compliance.test_lane_count); + if (ret < 0) { + DRM_ERROR("phytium_dp_dpcd_get_adjust_request fail: ret:%d\n", ret); + goto failed_dpcd_get_adjust_request; + } + phytium_dp_hw_set_lane_setting(phytium_dp, phytium_dp->compliance.test_link_rate, + phytium_dp->train_set[0]); + ret = phytium_dp_dpcd_set_lane_setting(phytium_dp, phytium_dp->train_set); + if (ret < 0) { + DRM_ERROR("phytium_dp_dpcd_set_lane_setting fail: ret:%d\n", ret); + goto failed_dpcd_set_lane_setting; + } + + /* config test pattern */ + phytium_dp_hw_set_test_pattern(phytium_dp, phytium_dp->compliance.test_lane_count, + test_pattern, test_80_bit_pattern, + sizeof(test_80_bit_pattern)); + ret = phytium_dp_dpcd_set_test_pattern(phytium_dp, test_pattern); + if (ret < 0) { + DRM_ERROR("phytium_dp_dpcd_set_test_pattern fail: ret:%d\n", ret); + goto failed_dpcd_set_tp; + } + + return DP_TEST_ACK; + +failed_dpcd_set_tp: + phytium_dp_hw_set_test_pattern(phytium_dp, phytium_dp->compliance.test_lane_count, + PHYTIUM_PHY_TP_NONE, test_80_bit_pattern, + sizeof(test_80_bit_pattern)); +failed_dpcd_set_link: +failed_dpcd_set_lane_setting: +failed_dpcd_get_adjust_request: +failed: + return DP_TEST_NAK; +} + +static void phytium_dp_handle_test_request(struct phytium_dp_device *phytium_dp) +{ + uint8_t response = DP_TEST_NAK; + uint8_t request = 0; + int status; + + status = drm_dp_dpcd_readb(&phytium_dp->aux, DP_TEST_REQUEST, &request); + if (status <= 0) { + DRM_DEBUG_KMS("Could not read test request from sink\n"); + goto update_status; + } + + switch (request) { + case DP_TEST_LINK_TRAINING: + case DP_TEST_LINK_VIDEO_PATTERN: + case DP_TEST_LINK_EDID_READ: + DRM_DEBUG_KMS("Not support test request '%02x'\n", request); + response = DP_TEST_NAK; + break; + case DP_TEST_LINK_PHY_TEST_PATTERN: + DRM_DEBUG_KMS("PHY_PATTERN test requested\n"); + response = phytium_dp_autotest_phy_pattern(phytium_dp); + break; + default: + DRM_DEBUG_KMS("Invalid test request '%02x'\n", request); + break; + } + +update_status: + status = drm_dp_dpcd_writeb(&phytium_dp->aux, DP_TEST_RESPONSE, response); + if (status <= 0) + DRM_DEBUG_KMS("Could not write test response to sink\n"); + +} + +static int phytium_dp_long_pulse(struct drm_connector *connector, bool hpd_raw_state) +{ + struct phytium_dp_device *phytium_dp = connector_to_dp_device(connector); + enum drm_connector_status status = connector->status; + bool video_enable = false; + uint32_t index = 0; + + if (phytium_dp->is_edp) + status = connector_status_connected; + else if (hpd_raw_state) { + if (!phytium_dp_needs_link_retrain(phytium_dp)) { + status = connector_status_connected; + goto out; + } + } else { + status = connector_status_disconnected; + goto out; + } + + if (!phytium_dp->is_edp) { + status = phytium_dp_detect_dpcd(phytium_dp); + if (status == connector_status_disconnected) + goto out; + + index = phytium_dp->num_common_rates-1; + phytium_dp->max_link_rate = phytium_dp->common_rates[index]; + phytium_dp->max_link_lane_count = phytium_dp->common_max_lane_count; + phytium_dp->link_rate = phytium_dp->max_link_rate; + phytium_dp->link_lane_count = phytium_dp->max_link_lane_count; + DRM_DEBUG_KMS("common_max_lane_count: %d, common_max_rate:%d\n", + phytium_dp->max_link_lane_count, phytium_dp->max_link_rate); + + video_enable = phytium_dp_hw_video_is_enable(phytium_dp); + phytium_dp_start_link_train(phytium_dp); + + if (video_enable) { + mdelay(2); + phytium_dp_hw_enable_video(phytium_dp); + } + } + +out: + return status; +} + +static int phytium_dp_short_pulse(struct drm_connector *connector) +{ + struct phytium_dp_device *phytium_dp = connector_to_dp_device(connector); + enum drm_connector_status status = connector->status; + u8 sink_irq_vector = 0; + bool video_enable = false; + + /* handle the test pattern */ + if (phytium_dp_get_sink_irq(phytium_dp, &sink_irq_vector) && + sink_irq_vector != 0) { + drm_dp_dpcd_writeb(&phytium_dp->aux, + DP_DEVICE_SERVICE_IRQ_VECTOR, + sink_irq_vector); + if (sink_irq_vector & DP_AUTOMATED_TEST_REQUEST) + phytium_dp_handle_test_request(phytium_dp); + if (sink_irq_vector & (DP_CP_IRQ | DP_SINK_SPECIFIC_IRQ)) + DRM_DEBUG_DRIVER("CP or sink specific irq unhandled\n"); + } + if (!phytium_dp_needs_link_retrain(phytium_dp)) { + status = connector_status_connected; + goto out; + } + + video_enable = phytium_dp_hw_video_is_enable(phytium_dp); + phytium_dp_start_link_train(phytium_dp); + if (video_enable) { + mdelay(2); + phytium_dp_hw_enable_video(phytium_dp); + } + +out: + return status; +} + +void phytium_dp_hpd_poll_handler(struct phytium_display_private *priv) +{ + struct drm_device *dev = priv->dev; + struct drm_connector_list_iter conn_iter; + struct drm_connector *connector; + enum drm_connector_status old_status; + bool changed = false; + + mutex_lock(&dev->mode_config.mutex); + DRM_DEBUG_KMS("running encoder hotplug poll functions\n"); + drm_connector_list_iter_begin(dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) { + if (connector->force) + continue; + old_status = connector->status; + connector->status = drm_helper_probe_detect(connector, NULL, false); + if (old_status != connector->status) { + const char *old, *new; + + old = drm_get_connector_status_name(old_status); + new = drm_get_connector_status_name(connector->status); + DRM_DEBUG_KMS("[CONNECTOR:%d:%s] status updated from %s to %s\n", + connector->base.id, + connector->name, + old, new); + changed = true; + } + } + drm_connector_list_iter_end(&conn_iter); + mutex_unlock(&dev->mode_config.mutex); + + if (changed) + drm_kms_helper_hotplug_event(dev); +} + +void phytium_dp_hpd_irq_setup(struct drm_device *dev, bool enable) +{ + struct phytium_dp_device *phytium_dp; + struct drm_encoder *encoder; + struct phytium_display_private *priv = dev->dev_private; + bool handler = false; + bool hpd_raw_state_old = false; + + /* We might have missed any hotplugs that happened, so polling and handler */ + if (enable) { + spin_lock_irq(&priv->hotplug_irq_lock); + + drm_for_each_encoder(encoder, dev) { + phytium_dp = encoder_to_dp_device(encoder); + if (!phytium_dp->dp_hpd_state.hpd_irq_enable) { + hpd_raw_state_old = phytium_dp->dp_hpd_state.hpd_raw_state; + phytium_dp_hw_get_hpd_state(phytium_dp); + if (phytium_dp->dp_hpd_state.hpd_event_state + || phytium_dp->dp_hpd_state.hpd_irq_state + || (hpd_raw_state_old != phytium_dp->dp_hpd_state.hpd_raw_state)) { + handler = true; + } + } + } + spin_unlock_irq(&priv->hotplug_irq_lock); + if (handler) + phytium_dp_hpd_poll_handler(priv); + } + + drm_for_each_encoder(encoder, dev) { + phytium_dp = encoder_to_dp_device(encoder); + phytium_dp_hw_hpd_irq_setup(phytium_dp, enable); + } +} + +void phytium_dp_hpd_work_func(struct work_struct *work) +{ + struct phytium_display_private *priv = + container_of(work, struct phytium_display_private, hotplug_work); + struct drm_device *dev = priv->dev; + struct drm_connector_list_iter conn_iter; + struct drm_connector *connector; + enum drm_connector_status old_status; + bool changed = false; + + mutex_lock(&dev->mode_config.mutex); + DRM_DEBUG_KMS("running encoder hotplug work functions\n"); + drm_connector_list_iter_begin(dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) { + if (connector->force) + continue; + old_status = connector->status; + connector->status = drm_helper_probe_detect(connector, NULL, false); + if (old_status != connector->status) { + const char *old, *new; + + old = drm_get_connector_status_name(old_status); + new = drm_get_connector_status_name(connector->status); + DRM_DEBUG_KMS("[CONNECTOR:%d:%s] status updated from %s to %s\n", + connector->base.id, + connector->name, + old, new); + changed = true; + } + } + drm_connector_list_iter_end(&conn_iter); + mutex_unlock(&dev->mode_config.mutex); + + if (changed) + drm_kms_helper_hotplug_event(dev); + + phytium_dp_hpd_irq_setup(dev, true); +} + +irqreturn_t phytium_dp_hpd_irq_handler(struct phytium_display_private *priv) +{ + struct drm_encoder *encoder = NULL; + struct phytium_dp_device *phytium_dp = NULL; + struct drm_device *dev = priv->dev; + bool handler = false; + + spin_lock(&priv->hotplug_irq_lock); + + drm_for_each_encoder(encoder, dev) { + phytium_dp = encoder_to_dp_device(encoder); + if (phytium_dp->dp_hpd_state.hpd_irq_enable) { + phytium_dp_hw_get_hpd_state(phytium_dp); + if (phytium_dp->dp_hpd_state.hpd_event_state + || phytium_dp->dp_hpd_state.hpd_irq_state) { + handler = true; + } + } + } + spin_unlock(&priv->hotplug_irq_lock); + + if (handler) { + phytium_dp_hpd_irq_setup(dev, false); + schedule_work(&priv->hotplug_work); + return IRQ_HANDLED; + } + return IRQ_NONE; +} + + +static void phytium_dp_fast_link_train_detect(struct phytium_dp_device *phytium_dp) +{ + phytium_dp->fast_train_support = !!(phytium_dp->dpcd[DP_MAX_DOWNSPREAD] + & DP_NO_AUX_HANDSHAKE_LINK_TRAINING); + DRM_DEBUG_KMS("fast link training %s\n", + phytium_dp->fast_train_support ? "supported" : "unsupported"); +} + +bool phytium_dp_fast_link_train(struct phytium_dp_device *phytium_dp) +{ + int ret = 0; + unsigned int training_pattern; + + /* clear the test pattern */ + phytium_dp_hw_set_test_pattern(phytium_dp, phytium_dp->link_lane_count, + PHYTIUM_PHY_TP_NONE, NULL, 0); + + /* config source and sink's link rate and lane count */ + phytium_dp_hw_set_link(phytium_dp, phytium_dp->link_lane_count, phytium_dp->link_rate); + + /* config source and sink's voltage swing and pre-emphasis(103-106) */ + phytium_dp_hw_set_lane_setting(phytium_dp, phytium_dp->link_rate, + phytium_dp->train_set[0]); + + /* config train pattern */ + phytium_dp_hw_set_train_pattern(phytium_dp, DP_TRAINING_PATTERN_1); + usleep_range(500, 600); + + training_pattern = phytium_dp_get_training_pattern(phytium_dp); + phytium_dp_hw_set_train_pattern(phytium_dp, training_pattern); + usleep_range(500, 600); + + phytium_dp_hw_set_train_pattern(phytium_dp, DP_TRAINING_PATTERN_DISABLE); + + if (dc_fast_training_check) { + unsigned char link_status[DP_LINK_STATUS_SIZE]; + + ret = drm_dp_dpcd_read(&phytium_dp->aux, DP_LANE0_1_STATUS, + link_status, DP_LINK_STATUS_SIZE); + if (ret < 0) { + DRM_ERROR("failed to get link status(DP_LANE0_1_STATUS)\n"); + return false; + } + + if (!drm_dp_clock_recovery_ok(link_status, phytium_dp->link_lane_count)) { + DRM_DEBUG_KMS("check clock recovery failed\n"); + return false; + } + + if (!drm_dp_channel_eq_ok(link_status, phytium_dp->link_lane_count)) { + DRM_DEBUG_KMS("check channel equalization failed\n"); + return false; + } + } + + return true; +} + +static enum drm_connector_status +phytium_connector_detect(struct drm_connector *connector, bool force) +{ + enum drm_connector_status status = connector->status; + struct phytium_dp_device *phytium_dp = connector_to_dp_device(connector); + bool hpd_event_state, hpd_irq_state, hpd_raw_state; + struct drm_device *dev = phytium_dp->dev; + struct phytium_display_private *priv = dev->dev_private; + bool plugged = true; + + spin_lock_irq(&priv->hotplug_irq_lock); + hpd_event_state = phytium_dp->dp_hpd_state.hpd_event_state; + hpd_irq_state = phytium_dp->dp_hpd_state.hpd_irq_state; + hpd_raw_state = phytium_dp->dp_hpd_state.hpd_raw_state; + phytium_dp->dp_hpd_state.hpd_event_state = false; + phytium_dp->dp_hpd_state.hpd_irq_state = false; + spin_unlock_irq(&priv->hotplug_irq_lock); + + if (hpd_event_state) + status = phytium_dp_long_pulse(connector, hpd_raw_state); + + if (hpd_irq_state) + status = phytium_dp_short_pulse(connector); + + if (status == connector_status_unknown) + status = connector_status_disconnected; + + if ((!phytium_dp->is_edp) && (!hpd_raw_state)) + status = connector_status_disconnected; + + if (connector->status != status) { + if ((status == connector_status_connected) && phytium_dp->has_audio) + plugged = true; + else + plugged = false; + + handle_plugged_change(phytium_dp, plugged); + } + + return status; +} + +static void +phytium_connector_destroy(struct drm_connector *connector) +{ + struct phytium_dp_device *phytium_dp = connector_to_dp_device(connector); + + drm_connector_cleanup(connector); + kfree(phytium_dp); +} + +static int +phytium_dp_connector_register(struct drm_connector *connector) +{ + int ret; + struct phytium_dp_device *phytium_dp = connector_to_dp_device(connector); + + phytium_dp_aux_init(phytium_dp); + if (phytium_dp->is_edp) { + phytium_edp_init_connector(phytium_dp); + ret = phytium_edp_backlight_device_register(phytium_dp); + if (ret) + DRM_ERROR("failed to register port(%d) backlight device(ret=%d)\n", + phytium_dp->port, ret); + } + + ret = phytium_debugfs_connector_add(connector); + if (ret) + DRM_ERROR("failed to register phytium connector debugfs(ret=%d)\n", ret); + + return 0; +} + +static void +phytium_dp_connector_unregister(struct drm_connector *connector) +{ + struct phytium_dp_device *phytium_dp = connector_to_dp_device(connector); + + if (phytium_dp->is_edp) { + phytium_edp_backlight_device_unregister(phytium_dp); + phytium_edp_fini_connector(phytium_dp); + } + drm_dp_aux_unregister(&phytium_dp->aux); +} + +static const struct drm_connector_funcs phytium_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .detect = phytium_connector_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = phytium_connector_destroy, + .reset = drm_atomic_helper_connector_reset, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, + .late_register = phytium_dp_connector_register, + .early_unregister = phytium_dp_connector_unregister, +}; + +static void phytium_dp_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted) +{ + struct phytium_dp_device *dp = encoder_to_dp_device(encoder); + + drm_mode_copy(&dp->mode, adjusted); +} + +static void phytium_edp_panel_poweron(struct phytium_dp_device *phytium_dp) +{ + phytium_panel_poweron(&phytium_dp->panel); +} + +static void phytium_edp_panel_poweroff(struct phytium_dp_device *phytium_dp) +{ + phytium_panel_poweroff(&phytium_dp->panel); +} + +static void phytium_edp_backlight_on(struct phytium_dp_device *phytium_dp) +{ + phytium_panel_enable_backlight(&phytium_dp->panel); +} + +static void phytium_edp_backlight_off(struct phytium_dp_device *phytium_dp) +{ + phytium_panel_disable_backlight(&phytium_dp->panel); +} + +static void phytium_encoder_disable(struct drm_encoder *encoder) +{ + struct phytium_dp_device *phytium_dp = encoder_to_dp_device(encoder); + + if (phytium_dp->is_edp) + phytium_edp_backlight_off(phytium_dp); + + phytium_dp_hw_disable_video(phytium_dp); + + mdelay(50); + + if (phytium_dp->is_edp) + phytium_edp_panel_poweroff(phytium_dp); +} + +void phytium_dp_adjust_link_train_parameter(struct phytium_dp_device *phytium_dp) +{ + struct drm_display_info *display_info = &phytium_dp->connector.display_info; + unsigned long link_bw, date_rate = 0, bs_limit, bs_request; + int rate = 0; + + bs_request = phytium_dp->mode.crtc_htotal/(phytium_dp->mode.crtc_clock/1000); + date_rate = (phytium_dp->mode.crtc_clock * display_info->bpc * 3)/8; + + for (;;) { + bs_limit = 8192 / (phytium_dp->link_rate/1000); + link_bw = phytium_dp->link_rate * phytium_dp->link_lane_count; + rate = 10 * date_rate / link_bw; + DRM_DEBUG_KMS("adjust link rate(%d), lane count(%d)\n", + phytium_dp->link_rate, phytium_dp->link_lane_count); + DRM_DEBUG_KMS("for crtc_clock(%d) bs_request(%ld) bs_limit(%ld) rate(%d)\n", + phytium_dp->mode.crtc_clock, bs_request, bs_limit, rate); + if ((link_dynamic_adjust && (bs_request < bs_limit) && rate < 10) || + ((!link_dynamic_adjust) && (rate < 10))) + break; + phytium_dp_get_link_train_fallback_values(phytium_dp); + } + + DRM_DEBUG_KMS("Try link training at Link Rate = %d, Lane count = %d\n", + phytium_dp->link_rate, phytium_dp->link_lane_count); +} + +static void phytium_encoder_enable(struct drm_encoder *encoder) +{ + struct phytium_dp_device *phytium_dp = encoder_to_dp_device(encoder); + int ret = 0; + + phytium_dp_hw_disable_video(phytium_dp); + + if (phytium_dp->is_edp) { + phytium_edp_panel_poweron(phytium_dp); + if (phytium_dp->fast_train_support) + phytium_dp_fast_link_train(phytium_dp); + else + ret = phytium_dp_start_link_train(phytium_dp); + mdelay(2); + phytium_dp_fast_link_train_detect(phytium_dp); + } else { + phytium_dp_adjust_link_train_parameter(phytium_dp); + ret = phytium_dp_start_link_train(phytium_dp); + mdelay(2); + } + + phytium_dp_hw_config_video(phytium_dp); + if (ret == 0) { + phytium_dp_hw_enable_video(phytium_dp); + if (phytium_dp->has_audio) + phytium_dp_hw_enable_audio(phytium_dp); + } + + if (phytium_dp->is_edp) { + phytium_edp_backlight_on(phytium_dp); + } +} + +enum drm_mode_status +phytium_encoder_mode_valid(struct drm_encoder *encoder, const struct drm_display_mode *mode) +{ + struct phytium_dp_device *phytium_dp = encoder_to_dp_device(encoder); + struct drm_display_info *display_info = &phytium_dp->connector.display_info; + unsigned int requested, actual; + + switch (display_info->bpc) { + case 10: + case 6: + case 8: + break; + default: + DRM_INFO("not support bpc(%d)\n", display_info->bpc); + display_info->bpc = 8; + break; + } + + if ((display_info->color_formats & DRM_COLOR_FORMAT_RGB444) == 0) { + DRM_INFO("not support color_format(%d)\n", display_info->color_formats); + display_info->color_formats = DRM_COLOR_FORMAT_RGB444; + } + + requested = mode->clock * display_info->bpc * 3 / 1000; + actual = phytium_dp->max_link_rate * phytium_dp->max_link_lane_count / 100; + actual = actual * 8 / 10; + if (requested >= actual) { + DRM_DEBUG_KMS("requested=%d, actual=%d, clock=%d\n", requested, actual, + mode->clock); + return MODE_CLOCK_HIGH; + } + + if (dc_fake_mode_enable && + (phytium_dp->native_mode.clock == mode->clock) && + (phytium_dp->native_mode.htotal == mode->htotal) && + (phytium_dp->native_mode.vtotal == mode->vtotal)) + return MODE_OK; + + if ((mode->hdisplay == 1600) && (mode->vdisplay == 900)) + return MODE_BAD_HVALUE; + + if ((mode->hdisplay == 1024) && (mode->clock > 78000)) + return MODE_BAD_HVALUE; + + if ((mode->hdisplay < 640) || (mode->vdisplay < 480)) + return MODE_BAD_HVALUE; + + return MODE_OK; +} + +static const struct drm_encoder_helper_funcs phytium_encoder_helper_funcs = { + .mode_set = phytium_dp_encoder_mode_set, + .disable = phytium_encoder_disable, + .enable = phytium_encoder_enable, + .mode_valid = phytium_encoder_mode_valid, +}; + +void phytium_dp_encoder_destroy(struct drm_encoder *encoder) +{ + struct phytium_dp_device *phytium_dp = encoder_to_dp_device(encoder); + + phytium_dp_audio_codec_fini(phytium_dp); + drm_encoder_cleanup(encoder); +} + +static const struct drm_encoder_funcs phytium_encoder_funcs = { + .destroy = phytium_dp_encoder_destroy, +}; + +static const struct dp_audio_n_m phytium_dp_audio_n_m[] = { + { 32000, 162000, 1024, 10125 }, + { 44100, 162000, 784, 5625 }, + { 48000, 162000, 512, 3375 }, + { 64000, 162000, 2048, 10125 }, + { 88200, 162000, 1568, 5625 }, + { 96000, 162000, 1024, 3375 }, + { 128000, 162000, 4096, 10125 }, + { 176400, 162000, 3136, 5625 }, + { 192000, 162000, 2048, 3375 }, + { 32000, 270000, 1024, 16875 }, + { 44100, 270000, 784, 9375 }, + { 48000, 270000, 512, 5625 }, + { 64000, 270000, 2048, 16875 }, + { 88200, 270000, 1568, 9375 }, + { 96000, 270000, 1024, 5625 }, + { 128000, 270000, 4096, 16875 }, + { 176400, 270000, 3136, 9375 }, + { 192000, 270000, 2048, 5625 }, + { 32000, 540000, 1024, 33750 }, + { 44100, 540000, 784, 18750 }, + { 48000, 540000, 512, 11250 }, + { 64000, 540000, 2048, 33750 }, + { 88200, 540000, 1568, 18750 }, + { 96000, 540000, 1024, 11250 }, + { 128000, 540000, 4096, 33750 }, + { 176400, 540000, 3136, 18750 }, + { 192000, 540000, 2048, 11250 }, + { 32000, 810000, 1024, 50625 }, + { 44100, 810000, 784, 28125 }, + { 48000, 810000, 512, 16875 }, + { 64000, 810000, 2048, 50625 }, + { 88200, 810000, 1568, 28125 }, + { 96000, 810000, 1024, 16875 }, + { 128000, 810000, 4096, 50625 }, + { 176400, 810000, 3136, 28125 }, + { 192000, 810000, 2048, 16875 }, +}; + +static int phytium_dp_audio_get_eld(struct device *dev, void *data, u8 *buf, size_t len) +{ + struct phytium_dp_device *phytium_dp = data; + + memcpy(buf, phytium_dp->connector.eld, min(sizeof(phytium_dp->connector.eld), len)); + + return 0; +} + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)) +static int phytium_dp_audio_digital_mute(struct device *dev, void *data, bool enable) +{ + struct phytium_dp_device *phytium_dp = data; + + phytium_dp_hw_audio_digital_mute(phytium_dp, enable); + + return 0; +} +#else +static int phytium_dp_audio_mute_stream(struct device *dev, void *data, bool enable, int direction) +{ + struct phytium_dp_device *phytium_dp = data; + + phytium_dp_hw_audio_digital_mute(phytium_dp, enable); + + return 0; +} +#endif + +const struct dp_audio_n_m *phytium_dp_audio_get_n_m(int link_rate, int sample_rate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(phytium_dp_audio_n_m); i++) { + if (sample_rate == phytium_dp_audio_n_m[i].sample_rate + && link_rate == phytium_dp_audio_n_m[i].link_rate) + return &phytium_dp_audio_n_m[i]; + } + + return NULL; +} + +static int phytium_dp_audio_hw_params(struct device *dev, void *data, + struct hdmi_codec_daifmt *daifmt, + struct hdmi_codec_params *params) +{ + struct phytium_dp_device *phytium_dp = data; + int ret = 0; + struct audio_info audio_info = { + .sample_width = params->sample_width, + .sample_rate = params->sample_rate, + .channels = params->channels, + }; + + if (daifmt->fmt != HDMI_I2S) { + DRM_ERROR("invalid audio format %d\n", daifmt->fmt); + ret = -EINVAL; + goto failed; + } + + ret = phytium_dp_hw_audio_hw_params(phytium_dp, audio_info); + +failed: + return ret; +} + +static void phytium_dp_audio_shutdown(struct device *dev, void *data) +{ + struct phytium_dp_device *phytium_dp = data; + + phytium_dp_hw_audio_shutdown(phytium_dp); +} + +static void handle_plugged_change(struct phytium_dp_device *phytium_dp, bool plugged) +{ + if (phytium_dp->plugged_cb && phytium_dp->codec_dev) + phytium_dp->plugged_cb(phytium_dp->codec_dev, plugged); +} + +static int phytium_dp_audio_hook_plugged_cb(struct device *dev, void *data, + hdmi_codec_plugged_cb fn, + struct device *codec_dev) +{ + struct phytium_dp_device *phytium_dp = data; + bool plugged; + + phytium_dp->plugged_cb = fn; + phytium_dp->codec_dev = codec_dev; + + if ((phytium_dp->connector.status == connector_status_connected) && phytium_dp->has_audio) + plugged = true; + else + plugged = false; + + handle_plugged_change(phytium_dp, plugged); + return 0; +} + + +static const struct hdmi_codec_ops phytium_audio_codec_ops = { + .hw_params = phytium_dp_audio_hw_params, + .audio_shutdown = phytium_dp_audio_shutdown, +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)) + .digital_mute = phytium_dp_audio_digital_mute, +#else + .mute_stream = phytium_dp_audio_mute_stream, +#endif + .get_eld = phytium_dp_audio_get_eld, + .hook_plugged_cb = phytium_dp_audio_hook_plugged_cb, +}; + +static int phytium_dp_audio_codec_init(struct phytium_dp_device *phytium_dp, + const int port) +{ + struct device *dev = phytium_dp->dev->dev; + struct hdmi_codec_pdata codec_data = { + .i2s = 1, + .spdif = 0, + .ops = &phytium_audio_codec_ops, + .max_i2s_channels = 2, + .data = phytium_dp, + }; + + phytium_dp->audio_pdev = platform_device_register_data(dev, HDMI_CODEC_DRV_NAME, + codec_id + port, + &codec_data, sizeof(codec_data)); + + return PTR_ERR_OR_ZERO(phytium_dp->audio_pdev); +} + +static void phytium_dp_audio_codec_fini(struct phytium_dp_device *phytium_dp) +{ + if (!PTR_ERR_OR_ZERO(phytium_dp->audio_pdev)) + platform_device_unregister(phytium_dp->audio_pdev); + phytium_dp->audio_pdev = NULL; +} + +static long phytium_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) +{ + struct phytium_dp_device *phytium_dp = container_of(aux, struct phytium_dp_device, aux); + long ret = 0; + + DRM_DEBUG_KMS("msg->size: 0x%lx\n", msg->size); + + if (WARN_ON(msg->size > 16)) + return -E2BIG; + + switch (msg->request & ~DP_AUX_I2C_MOT) { + case DP_AUX_NATIVE_WRITE: + case DP_AUX_I2C_WRITE: + case DP_AUX_I2C_WRITE_STATUS_UPDATE: + ret = phytium_dp_hw_aux_transfer_write(phytium_dp, msg); + DRM_DEBUG_KMS("aux write reply:0x%x ret:0x%lx\n", msg->reply, ret); + break; + case DP_AUX_NATIVE_READ: + case DP_AUX_I2C_READ: + ret = phytium_dp_hw_aux_transfer_read(phytium_dp, msg); + DRM_DEBUG_KMS("aux read ret:0x%lx\n", ret); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static void phytium_dp_aux_init(struct phytium_dp_device *phytium_dp) +{ + drm_dp_aux_init(&phytium_dp->aux); + phytium_dp->aux.name = kasprintf(GFP_KERNEL, "dp-%d", phytium_dp->port); + phytium_dp->aux.transfer = phytium_dp_aux_transfer; +} + +int phytium_get_encoder_crtc_mask(struct phytium_dp_device *phytium_dp, int port) +{ + struct drm_device *dev = phytium_dp->dev; + struct phytium_display_private *priv = dev->dev_private; + int i, mask = 0; + + for_each_pipe_masked(priv, i) { + if (i != port) + mask++; + else + break; + } + + return BIT(mask); +} + +static bool phytium_dp_is_edp(struct phytium_dp_device *phytium_dp, int port) +{ + struct drm_device *dev = phytium_dp->dev; + struct phytium_display_private *priv = dev->dev_private; + + if (priv->info.edp_mask & BIT(port)) + return true; + else + return false; +} + +static bool phytium_edp_init_connector(struct phytium_dp_device *phytium_dp) +{ + enum drm_connector_status status; + struct drm_connector *connector = &phytium_dp->connector; + + phytium_edp_panel_poweron(phytium_dp); + + status = phytium_dp_detect_dpcd(phytium_dp); + if (status == connector_status_disconnected) { + DRM_ERROR("detect edp dpcd failed\n"); + return false; + } + + phytium_dp->edp_edid = drm_get_edid(connector, &phytium_dp->aux.ddc); + if (!phytium_dp->edp_edid) { + DRM_ERROR("get edp edid failed\n"); + return false; + } + + connector->status = status; + phytium_dp->max_link_rate = phytium_dp->common_rates[phytium_dp->num_common_rates-1]; + phytium_dp->max_link_lane_count = phytium_dp->common_max_lane_count; + phytium_dp->link_rate = phytium_dp->max_link_rate; + phytium_dp->link_lane_count = phytium_dp->max_link_lane_count; + DRM_DEBUG_KMS("common_max_lane_count: %d, common_max_rate:%d\n", + phytium_dp->max_link_lane_count, phytium_dp->max_link_rate); + + return true; +} + +static void phytium_edp_fini_connector(struct phytium_dp_device *phytium_dp) +{ + if (phytium_dp->edp_edid) + kfree(phytium_dp->edp_edid); + + phytium_dp->edp_edid = NULL; + phytium_edp_panel_poweroff(phytium_dp); + + return; +} + +int phytium_dp_resume(struct drm_device *drm_dev) +{ + struct phytium_dp_device *phytium_dp; + struct drm_encoder *encoder; + int ret = 0; + + drm_for_each_encoder(encoder, drm_dev) { + phytium_dp = encoder_to_dp_device(encoder); + if (phytium_dp->is_edp) { + phytium_edp_backlight_off(phytium_dp); + phytium_edp_panel_poweroff(phytium_dp); + } + ret = phytium_dp_hw_init(phytium_dp); + if (ret) { + DRM_ERROR("failed to initialize dp %d\n", phytium_dp->port); + return -EIO; + } + } + + return 0; +} + +int phytium_dp_init(struct drm_device *dev, int port) +{ + struct phytium_display_private *priv = dev->dev_private; + struct phytium_dp_device *phytium_dp = NULL; + int ret, type; + + DRM_DEBUG_KMS("%s: port %d\n", __func__, port); + phytium_dp = kzalloc(sizeof(*phytium_dp), GFP_KERNEL); + if (!phytium_dp) { + ret = -ENOMEM; + goto failed_malloc_dp; + } + + phytium_dp->dev = dev; + phytium_dp->port = port; + + if (IS_PX210(priv)) { + px210_dp_func_register(phytium_dp); + priv->dp_reg_base[port] = PX210_DP_BASE(port); + priv->phy_access_base[port] = PX210_PHY_ACCESS_BASE(port); + } else if (IS_PE220X(priv)) { + pe220x_dp_func_register(phytium_dp); + priv->dp_reg_base[port] = PE220X_DP_BASE(port); + priv->phy_access_base[port] = PE220X_PHY_ACCESS_BASE(port); + } + + if (phytium_dp_is_edp(phytium_dp, port)) { + phytium_dp->is_edp = true; + type = DRM_MODE_CONNECTOR_eDP; + phytium_dp_panel_init_backlight_funcs(phytium_dp); + phytium_edp_backlight_off(phytium_dp); + phytium_edp_panel_poweroff(phytium_dp); + } else { + phytium_dp->is_edp = false; + type = DRM_MODE_CONNECTOR_DisplayPort; + } + + ret = phytium_dp_hw_init(phytium_dp); + if (ret) { + DRM_ERROR("failed to initialize dp %d\n", phytium_dp->port); + goto failed_init_dp; + } + + ret = drm_encoder_init(dev, &phytium_dp->encoder, + &phytium_encoder_funcs, + DRM_MODE_ENCODER_TMDS, "DP %d", port); + if (ret) { + DRM_ERROR("failed to initialize encoder with drm\n"); + goto failed_encoder_init; + } + drm_encoder_helper_add(&phytium_dp->encoder, &phytium_encoder_helper_funcs); + phytium_dp->encoder.possible_crtcs = phytium_get_encoder_crtc_mask(phytium_dp, port); + + phytium_dp->connector.dpms = DRM_MODE_DPMS_OFF; + phytium_dp->connector.polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT; + ret = drm_connector_init(dev, &phytium_dp->connector, &phytium_connector_funcs, + type); + if (ret) { + DRM_ERROR("failed to initialize connector with drm\n"); + goto failed_connector_init; + } + drm_connector_helper_add(&phytium_dp->connector, &phytium_connector_helper_funcs); + drm_connector_attach_encoder(&phytium_dp->connector, &phytium_dp->encoder); + + ret = phytium_dp_audio_codec_init(phytium_dp, port); + if (ret) { + DRM_ERROR("failed to initialize audio codec\n"); + goto failed_connector_init; + } + + phytium_dp->train_retry_count = 0; + INIT_WORK(&phytium_dp->train_retry_work, phytium_dp_train_retry_work_fn); + drm_connector_register(&phytium_dp->connector); + + return 0; +failed_connector_init: +failed_encoder_init: +failed_init_dp: + kfree(phytium_dp); +failed_malloc_dp: + return ret; +} diff --git a/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_dp.h b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_dp.h new file mode 100644 index 000000000..3433f2944 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_dp.h @@ -0,0 +1,155 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Phytium display drm driver + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#ifndef __PHYTIUM_DP_H__ +#define __PHYTIUM_DP_H__ + +#include +#include +#include + +struct phytium_dp_device; + +#include "phytium_panel.h" + +struct audio_info { + int sample_rate; + int channels; + int sample_width; +}; + +struct dp_audio_n_m { + int sample_rate; + int link_rate; + u16 m; + u16 n; +}; + +struct phytium_dp_compliance { + unsigned long test_type; + uint32_t test_link_rate; + u8 test_lane_count; + bool test_active; + u8 reserve[2]; +}; + +struct phytium_dp_func { + uint8_t (*dp_hw_get_source_lane_count)(struct phytium_dp_device *phytium_dp); + int (*dp_hw_reset)(struct phytium_dp_device *phytium_dp); + bool (*dp_hw_spread_is_enable)(struct phytium_dp_device *phytium_dp); + int (*dp_hw_set_backlight)(struct phytium_dp_device *phytium_dp, uint32_t level); + uint32_t (*dp_hw_get_backlight)(struct phytium_dp_device *phytium_dp); + void (*dp_hw_disable_backlight)(struct phytium_dp_device *phytium_dp); + void (*dp_hw_enable_backlight)(struct phytium_dp_device *phytium_dp); + void (*dp_hw_poweroff_panel)(struct phytium_dp_device *phytium_dp); + void (*dp_hw_poweron_panel)(struct phytium_dp_device *phytium_dp); + int (*dp_hw_init_phy)(struct phytium_dp_device *phytium_dp); + void (*dp_hw_set_phy_lane_setting)(struct phytium_dp_device *phytium_dp, + uint32_t link_rate, uint8_t train_set); + int (*dp_hw_set_phy_lane_and_rate)(struct phytium_dp_device *phytium_dp, + uint8_t link_lane_count, + uint32_t link_rate); +}; + +struct phytium_dp_hpd_state { + bool hpd_event_state; + bool hpd_irq_state; + bool hpd_raw_state; + bool hpd_irq_enable; +}; + +struct phytium_dp_device { + struct drm_device *dev; + struct drm_encoder encoder; + struct drm_connector connector; + int port; + struct drm_display_mode mode; + bool link_trained; + bool detect_done; + bool is_edp; + bool reserve0; + struct drm_dp_aux aux; + unsigned char dpcd[DP_RECEIVER_CAP_SIZE]; + uint8_t edp_dpcd[EDP_DISPLAY_CTL_CAP_SIZE]; + unsigned char downstream_ports[DP_MAX_DOWNSTREAM_PORTS]; + unsigned char sink_count; + + int *source_rates; + int num_source_rates; + int sink_rates[DP_MAX_SUPPORTED_RATES]; + int num_sink_rates; + int common_rates[DP_MAX_SUPPORTED_RATES]; + int num_common_rates; + + int source_max_lane_count; + int sink_max_lane_count; + int common_max_lane_count; + + int max_link_rate; + int max_link_lane_count; + int link_rate; + int link_lane_count; + struct work_struct train_retry_work; + int train_retry_count; + uint32_t trigger_train_fail; + + unsigned char train_set[4]; + struct edid *edp_edid; + bool has_audio; + bool fast_train_support; + bool hw_spread_enable; + bool reserve[1]; + struct platform_device *audio_pdev; + struct audio_info audio_info; + hdmi_codec_plugged_cb plugged_cb; + struct device *codec_dev; + struct phytium_dp_compliance compliance; + struct phytium_dp_func *funcs; + struct phytium_dp_hpd_state dp_hpd_state; + + struct phytium_panel panel; + struct drm_display_mode native_mode; +}; + +union phytium_phy_tp { + struct { + /* DpcdPhyTestPatterns. This field is 2 bits for DP1.1 + * and 3 bits for DP1.2. + */ + uint8_t PATTERN :3; + uint8_t RESERVED :5; + } bits; + uint8_t raw; +}; + +/* PHY test patterns + * The order of test patterns follows DPCD register PHY_TEST_PATTERN (0x248) + */ +enum phytium_dpcd_phy_tp { + PHYTIUM_PHY_TP_NONE = 0, + PHYTIUM_PHY_TP_D10_2, + PHYTIUM_PHY_TP_SYMBOL_ERROR, + PHYTIUM_PHY_TP_PRBS7, + PHYTIUM_PHY_TP_80BIT_CUSTOM, + PHYTIUM_PHY_TP_CP2520_1, + PHYTIUM_PHY_TP_CP2520_2, + PHYTIUM_PHY_TP_CP2520_3, +}; +#define PHYTIUM_DP_AUDIO_ID (('P' << 24) + ('H' << 16) + ('Y' << 8)) +#define encoder_to_dp_device(x) container_of(x, struct phytium_dp_device, encoder) +#define connector_to_dp_device(x) container_of(x, struct phytium_dp_device, connector) +#define panel_to_dp_device(x) container_of(x, struct phytium_dp_device, panel) +#define train_retry_to_dp_device(x) container_of(x, struct phytium_dp_device, train_retry_work) +void phytium_phy_writel(struct phytium_dp_device *phytium_dp, uint32_t address, uint32_t data); +uint32_t phytium_phy_readl(struct phytium_dp_device *phytium_dp, uint32_t address); + +int phytium_dp_init(struct drm_device *dev, int pipe); +int phytium_dp_resume(struct drm_device *drm_dev); +void phytium_dp_hpd_irq_setup(struct drm_device *dev, bool enable); +irqreturn_t phytium_dp_hpd_irq_handler(struct phytium_display_private *priv); +void phytium_dp_hpd_work_func(struct work_struct *work); +const struct dp_audio_n_m *phytium_dp_audio_get_n_m(int link_rate, int sample_rate); +#endif /* __PHYTIUM_DP_H__ */ diff --git a/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_fb.c b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_fb.c new file mode 100644 index 000000000..724c02720 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_fb.c @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Phytium display drm driver + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include "phytium_display_drv.h" +#include "phytium_fb.h" +#include "phytium_gem.h" + +static int +phytium_fb_create_handle(struct drm_framebuffer *fb, struct drm_file *file_priv, + unsigned int *handle) +{ + struct phytium_framebuffer *phytium_fb = to_phytium_framebuffer(fb); + + return drm_gem_handle_create(file_priv, &phytium_fb->phytium_gem_obj[0]->base, handle); +} + +static void phytium_fb_destroy(struct drm_framebuffer *fb) +{ + struct phytium_framebuffer *phytium_fb = to_phytium_framebuffer(fb); + int i, num_planes; + struct drm_gem_object *obj = NULL; + const struct drm_format_info *info; + + info = drm_format_info(fb->format->format); + num_planes = info ? info->num_planes : 1; + + for (i = 0; i < num_planes; i++) { + obj = &phytium_fb->phytium_gem_obj[i]->base; + if (obj) +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)) + drm_gem_object_put(obj); +#else + drm_gem_object_unreference_unlocked(obj); +#endif + } + + drm_framebuffer_cleanup(fb); + kfree(phytium_fb); +} + +static struct drm_framebuffer_funcs viv_fb_funcs = { + .create_handle = phytium_fb_create_handle, + .destroy = phytium_fb_destroy, +}; + +struct phytium_framebuffer * +phytium_fb_alloc(struct drm_device *dev, const struct drm_mode_fb_cmd2 *mode_cmd, + struct phytium_gem_object **phytium_gem_obj, unsigned int num_planes) +{ + struct phytium_framebuffer *phytium_fb; + int ret = 0, i; + + phytium_fb = kzalloc(sizeof(*phytium_fb), GFP_KERNEL); + if (!phytium_fb) + return ERR_PTR(-ENOMEM); + + drm_helper_mode_fill_fb_struct(dev, &phytium_fb->base, mode_cmd); + + ret = drm_framebuffer_init(dev, &phytium_fb->base, &viv_fb_funcs); + + if (ret) { + DRM_ERROR("Failed to initialize framebuffer: %d\n", ret); + kfree(phytium_fb); + return ERR_PTR(ret); + } + + for (i = 0; i < num_planes; i++) { + phytium_fb->phytium_gem_obj[i] = phytium_gem_obj[i]; + phytium_fb->base.obj[i] = &phytium_gem_obj[i]->base; + } + return phytium_fb; +} + +struct drm_framebuffer * +phytium_fb_create(struct drm_device *dev, struct drm_file *file_priv, + const struct drm_mode_fb_cmd2 *mode_cmd) +{ + int ret = 0, i, num_planes; + struct drm_gem_object *obj; + unsigned int hsub, vsub, size; + struct phytium_gem_object *phytium_gem_obj[PHYTIUM_FORMAT_MAX_PLANE] = {0}; + struct phytium_framebuffer *phytium_fb; + struct phytium_display_private *priv = dev->dev_private; + const struct drm_format_info *info; + + info = drm_format_info(mode_cmd->pixel_format); + hsub = info ? info->hsub : 1; + vsub = info ? info->vsub : 1; + num_planes = info ? info->num_planes : 1; + num_planes = min(num_planes, PHYTIUM_FORMAT_MAX_PLANE); + + for (i = 0; i < num_planes; i++) { + unsigned int height = mode_cmd->height / (i ? vsub : 1); + + size = height * mode_cmd->pitches[i] + mode_cmd->offsets[i]; + obj = drm_gem_object_lookup(file_priv, mode_cmd->handles[i]); + if (!obj) { + DRM_ERROR("Failed to lookup GEM object\n"); + ret = -ENXIO; + goto error; + } + + if (obj->size < size) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)) + drm_gem_object_put(obj); +#else + drm_gem_object_unreference_unlocked(obj); +#endif + ret = -EINVAL; + goto error; + } + + phytium_gem_obj[i] = to_phytium_gem_obj(obj); + + ret = priv->dc_hw_fb_format_check(mode_cmd, i); + if (ret < 0) + goto error; + } + + phytium_fb = phytium_fb_alloc(dev, mode_cmd, phytium_gem_obj, i); + if (IS_ERR(phytium_fb)) { + DRM_DEBUG_KMS("phytium_fb_alloc failed\n"); + ret = PTR_ERR(phytium_fb); + goto error; + } + + return &phytium_fb->base; +error: + for (i--; i >= 0; i--) +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)) + drm_gem_object_put(&phytium_gem_obj[i]->base); +#else + drm_gem_object_unreference_unlocked(&phytium_gem_obj[i]->base); +#endif + + return ERR_PTR(ret); +} diff --git a/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_fb.h b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_fb.h new file mode 100644 index 000000000..054b0ab15 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_fb.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Phytium display drm driver + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#ifndef __PHYTIUM_FB_H__ +#define __PHYTIUM_FB_H__ + +#include + +struct phytium_framebuffer { + struct drm_framebuffer base; + struct phytium_gem_object *phytium_gem_obj[PHYTIUM_FORMAT_MAX_PLANE]; +}; + +#define to_phytium_framebuffer(fb) container_of(fb, struct phytium_framebuffer, base) + +struct phytium_framebuffer *phytium_fb_alloc(struct drm_device *dev, + const struct drm_mode_fb_cmd2 *mode_cmd, + struct phytium_gem_object **phytium_gem_obj, + unsigned int num_planes); + +struct drm_framebuffer *phytium_fb_create(struct drm_device *dev, struct drm_file *file_priv, + const struct drm_mode_fb_cmd2 *mode_cmd); +#endif /* __PHYTIUM_FB_H__ */ diff --git a/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_fbdev.c b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_fbdev.c new file mode 100644 index 000000000..dd0c29208 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_fbdev.c @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Phytium display drm driver + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include "phytium_display_drv.h" +#include "phytium_gem.h" +#include "phytium_fb.h" + + +#define PHYTIUM_MAX_CONNECTOR 1 +#define helper_to_drm_private(x) container_of(x, struct phytium_display_private, fbdev_helper) + +static int phytium_fbdev_mmap(struct fb_info *info, struct vm_area_struct *vma) +{ + struct drm_fb_helper *helper = info->par; + struct phytium_display_private *priv = helper_to_drm_private(helper); + + return phytium_gem_mmap_obj(&priv->fbdev_phytium_gem->base, vma); +} + +static struct fb_ops phytium_fbdev_ops = { + .owner = THIS_MODULE, + DRM_FB_HELPER_DEFAULT_OPS, + .fb_mmap = phytium_fbdev_mmap, + .fb_fillrect = drm_fb_helper_cfb_fillrect, + .fb_copyarea = drm_fb_helper_cfb_copyarea, + .fb_imageblit = drm_fb_helper_cfb_imageblit, +}; + +static int +phytium_drm_fbdev_create(struct drm_fb_helper *helper, struct drm_fb_helper_surface_size *sizes) +{ + struct phytium_display_private *priv = helper_to_drm_private(helper); + struct drm_device *dev = helper->dev; + unsigned int bytes_per_pixel; + struct drm_mode_fb_cmd2 mode_cmd = {0}; + struct phytium_framebuffer *phytium_fb = NULL; + struct fb_info *fbi = NULL; + struct drm_framebuffer *fb = NULL; + size_t size = 0; + int ret = 0; + unsigned long offset; + + bytes_per_pixel = DIV_ROUND_UP(sizes->surface_bpp, 8); + mode_cmd.width = sizes->surface_width; + mode_cmd.height = sizes->surface_height; + mode_cmd.pitches[0] = ALIGN(sizes->surface_width * bytes_per_pixel, 128); + mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, sizes->surface_depth); + size = PAGE_ALIGN(mode_cmd.pitches[0] * mode_cmd.height); + + ret = mutex_lock_interruptible(&dev->struct_mutex); + if (ret < 0) { + DRM_ERROR("failed to get mutex lock\n"); + return ret; + } + + priv->fbdev_phytium_gem = phytium_gem_create_object(dev, size); + if (!priv->fbdev_phytium_gem) { + DRM_ERROR("failed to create gem object\n"); + return -ENOMEM; + } + mutex_unlock(&dev->struct_mutex); + + fbi = drm_fb_helper_alloc_fbi(helper); + if (IS_ERR(fbi)) { + DRM_DEV_ERROR(dev->dev, "Failed to create framebuffer info."); + ret = PTR_ERR(fbi); + goto out; + } + + phytium_fb = phytium_fb_alloc(dev, &mode_cmd, &priv->fbdev_phytium_gem, 1); + if (IS_ERR(phytium_fb)) { + DRM_DEV_ERROR(dev->dev, "Failed to alloc DRM framebuffer.\n"); + ret = PTR_ERR(phytium_fb); + goto out; + } + + helper->fb = &(phytium_fb->base); + fbi->par = helper; + fbi->flags = FBINFO_FLAG_DEFAULT; + fbi->fbops = &phytium_fbdev_ops; + + fb = helper->fb; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)) + drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->format->depth); + drm_fb_helper_fill_var(fbi, helper, sizes->fb_width, sizes->fb_height); +#else + drm_fb_helper_fill_info(fbi, helper, sizes); +#endif + + offset = fbi->var.xoffset * bytes_per_pixel; + offset += fbi->var.yoffset * fb->pitches[0]; + dev->mode_config.fb_base = 0; + fbi->screen_base = priv->fbdev_phytium_gem->vaddr + offset; + fbi->screen_size = priv->fbdev_phytium_gem->base.size; + fbi->fix.smem_len = priv->fbdev_phytium_gem->base.size; + DRM_DEBUG_KMS("FB [%dx%d]-%d kvaddr=%pa offset=%ld size=%zu\n", fb->width, fb->height, + fb->format->depth, &priv->fbdev_phytium_gem->iova, offset, size); + fbi->skip_vt_switch = true; + + return 0; +out: + phytium_gem_free_object(&priv->fbdev_phytium_gem->base); + return ret; +} + +static const struct drm_fb_helper_funcs phytium_drm_fb_helper_funcs = { + .fb_probe = phytium_drm_fbdev_create, +}; + +int phytium_drm_fbdev_init(struct drm_device *dev) +{ + struct phytium_display_private *priv = dev->dev_private; + struct drm_fb_helper *helper; + int ret; + + if (!dev->mode_config.num_crtc || !dev->mode_config.num_connector) + return -EINVAL; + + helper = &priv->fbdev_helper; + drm_fb_helper_prepare(dev, helper, &phytium_drm_fb_helper_funcs); + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)) + ret = drm_fb_helper_init(dev, helper, PHYTIUM_MAX_CONNECTOR); +#else + ret = drm_fb_helper_init(dev, helper); +#endif + if (ret < 0) { + DRM_DEV_ERROR(dev->dev, "Failed to initialize drm fb helper -ret %d\n", ret); + return ret; + } + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)) + ret = drm_fb_helper_single_add_all_connectors(helper); + if (ret < 0) { + DRM_DEV_ERROR(dev->dev, "Failed to add connectors - %d/\n", ret); + goto err_drm_fb_helper_fini; + } +#endif + ret = drm_fb_helper_initial_config(helper, 32); + return ret; + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)) +err_drm_fb_helper_fini: + drm_fb_helper_fini(helper); + return ret; +#endif +} + +void phytium_drm_fbdev_fini(struct drm_device *dev) +{ + struct phytium_display_private *priv = dev->dev_private; + struct drm_fb_helper *helper; + + helper = &priv->fbdev_helper; + drm_fb_helper_unregister_fbi(helper); + + if (helper->fb) + drm_framebuffer_put(helper->fb); + + drm_fb_helper_fini(helper); +} diff --git a/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_fbdev.h b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_fbdev.h new file mode 100644 index 000000000..81070502c --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_fbdev.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Phytium display drm driver + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#ifndef _PHYTIUM_FBDEV_H +#define _PHYTIUM_FBDEV_H + +int phytium_drm_fbdev_init(struct drm_device *dev); +void phytium_drm_fbdev_fini(struct drm_device *dev); + +#endif /* _PHYTIUM_FBDEV_H */ diff --git a/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_gem.c b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_gem.c new file mode 100644 index 000000000..cc51b0f96 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_gem.c @@ -0,0 +1,535 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Phytium display drm driver + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "phytium_display_drv.h" +#include "phytium_gem.h" + +#define VRAM_POOL_ALLOC_ORDER 12 + +int phytium_memory_pool_alloc(struct phytium_display_private *priv, void **pvaddr, + phys_addr_t *phys_addr, uint64_t size) +{ + unsigned long vaddr; + + vaddr = gen_pool_alloc(priv->memory_pool, size); + if (!vaddr) + return -ENOMEM; + + *phys_addr = gen_pool_virt_to_phys(priv->memory_pool, vaddr); + + *pvaddr = (void *)vaddr; + return 0; +} + +void phytium_memory_pool_free(struct phytium_display_private *priv, void *vaddr, uint64_t size) +{ + gen_pool_free(priv->memory_pool, (unsigned long)vaddr, size); +} + +int phytium_memory_pool_init(struct device *dev, struct phytium_display_private *priv) +{ + int ret = 0; + + priv->memory_pool = gen_pool_create(VRAM_POOL_ALLOC_ORDER, -1); + if (priv->memory_pool == NULL) { + DRM_ERROR("fail to create memory pool\n"); + ret = -1; + goto failed_create_pool; + } + + ret = gen_pool_add_virt(priv->memory_pool, (unsigned long)priv->pool_virt_addr, + priv->pool_phys_addr, priv->pool_size, -1); + if (ret) { + DRM_ERROR("fail to add vram pool\n"); + ret = -1; + goto failed_add_pool_virt; + } + + return 0; + +failed_add_pool_virt: + gen_pool_destroy(priv->memory_pool); + +failed_create_pool: + return ret; +} + +void phytium_memory_pool_fini(struct device *dev, struct phytium_display_private *priv) +{ + gen_pool_destroy(priv->memory_pool); +} + +struct sg_table * +phytium_gem_prime_get_sg_table(struct drm_gem_object *obj) +{ + struct phytium_gem_object *phytium_gem_obj = to_phytium_gem_obj(obj); + struct sg_table *sgt; + struct drm_device *dev = obj->dev; + int ret; + struct page *page = NULL; + + sgt = kzalloc(sizeof(*sgt), GFP_KERNEL); + if (!sgt) { + DRM_DEBUG_KMS("malloc sgt fail\n"); + return ERR_PTR(-ENOMEM); + } + + if ((phytium_gem_obj->memory_type == MEMORY_TYPE_VRAM) || + (phytium_gem_obj->memory_type == MEMORY_TYPE_SYSTEM_CARVEOUT)) { + ret = sg_alloc_table(sgt, 1, GFP_KERNEL); + if (ret) { + DRM_ERROR("failed to allocate sg\n"); + goto sgt_free; + } + page = phys_to_page(phytium_gem_obj->phys_addr); + sg_set_page(sgt->sgl, page, PAGE_ALIGN(phytium_gem_obj->size), 0); + } else if (phytium_gem_obj->memory_type == MEMORY_TYPE_SYSTEM_UNIFIED) { + ret = dma_get_sgtable_attrs(dev->dev, sgt, phytium_gem_obj->vaddr, + phytium_gem_obj->iova, phytium_gem_obj->size, + DMA_ATTR_WRITE_COMBINE); + if (ret) { + DRM_ERROR("failed to allocate sgt, %d\n", ret); + goto sgt_free; + } + } + + return sgt; +sgt_free: + kfree(sgt); + return ERR_PTR(ret); +} + +struct drm_gem_object * +phytium_gem_prime_import_sg_table(struct drm_device *dev, + struct dma_buf_attachment *attach, + struct sg_table *sgt) +{ + struct phytium_gem_object *phytium_gem_obj = NULL; + struct scatterlist *s; + dma_addr_t expected; + int ret, i; + + phytium_gem_obj = kzalloc(sizeof(*phytium_gem_obj), GFP_KERNEL); + if (!phytium_gem_obj) { + DRM_ERROR("failed to allocate phytium_gem_obj\n"); + ret = -ENOMEM; + goto failed_malloc; + } + + ret = drm_gem_object_init(dev, &phytium_gem_obj->base, attach->dmabuf->size); + if (ret) { + DRM_ERROR("failed to initialize drm gem object: %d\n", ret); + goto failed_object_init; + } + + expected = sg_dma_address(sgt->sgl); + for_each_sg(sgt->sgl, s, sgt->nents, i) { + if (sg_dma_address(s) != expected) { + DRM_ERROR("sg_table is not contiguous"); + ret = -EINVAL; + goto failed_check_continue; + } + expected = sg_dma_address(s) + sg_dma_len(s); + } + + phytium_gem_obj->iova = sg_dma_address(sgt->sgl); + phytium_gem_obj->sgt = sgt; + + return &phytium_gem_obj->base; +failed_check_continue: + drm_gem_object_release(&phytium_gem_obj->base); +failed_object_init: + kfree(phytium_gem_obj); +failed_malloc: + return ERR_PTR(ret); +} + +void *phytium_gem_prime_vmap(struct drm_gem_object *obj) +{ + struct phytium_gem_object *phytium_obj = to_phytium_gem_obj(obj); + + return phytium_obj->vaddr; +} + +void phytium_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr) +{ + return; +} + +int phytium_gem_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma) +{ + int ret = 0; + + ret = drm_gem_mmap_obj(obj, obj->size, vma); + if (ret < 0) + return ret; + + return phytium_gem_mmap_obj(obj, vma); +} + +static void phytium_dma_callback(void *callback_param) +{ + struct completion *comp = callback_param; + + complete(comp); +} + +int phytium_dma_transfer(struct drm_device *drm_dev, int dev_to_mem, void *addr, + dma_addr_t iova, uint64_t size) +{ + struct phytium_display_private *priv = drm_dev->dev_private; + struct dma_chan *dma_chan = priv->dma_chan; + struct sg_table st; + struct scatterlist *sgl; + int ret = 0, timeout; + uint32_t nents, i; + struct dma_slave_config cfg = {0}; + struct dma_async_tx_descriptor *desc; + struct completion comp; + enum dma_data_direction dir; + size_t min = 0; + + nents = DIV_ROUND_UP(size, PAGE_SIZE); + ret = sg_alloc_table(&st, nents, GFP_KERNEL); + if (ret) { + DRM_ERROR("failed to allocate sg_table\n"); + ret = -ENOMEM; + goto failed_sg_alloc_table; + } + + for_each_sg(st.sgl, sgl, st.nents, i) { + min = min_t(size_t, size, PAGE_SIZE - offset_in_page(addr)); + sg_set_page(sgl, vmalloc_to_page(addr), min, offset_in_page(addr)); + addr += min; + size -= min; + } + + memset(&cfg, 0, sizeof(cfg)); + if (dev_to_mem) { + cfg.direction = DMA_DEV_TO_MEM; + cfg.src_addr = iova; + cfg.dst_addr = 0; + dir = DMA_FROM_DEVICE; + } else { + cfg.direction = DMA_MEM_TO_DEV; + cfg.src_addr = 0; + cfg.dst_addr = iova; + dir = DMA_TO_DEVICE; + } + + dmaengine_slave_config(dma_chan, &cfg); + + nents = dma_map_sg(dma_chan->device->dev, st.sgl, st.nents, dir); + if (!nents) { + DRM_DEV_ERROR(drm_dev->dev, "failed to dma_map_sg for dmaengine\n"); + ret = -EINVAL; + goto failed_dma_map_sg; + } + st.nents = nents; + dma_sync_sg_for_device(dma_chan->device->dev, st.sgl, st.nents, dir); + + sgl = st.sgl; + desc = dmaengine_prep_slave_sg(dma_chan, + st.sgl, + st.nents, + cfg.direction, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) { + DRM_DEV_ERROR(drm_dev->dev, "failed to dmaengine_prep_slave_sg\n"); + ret = -EINVAL; + goto failed_prep_slave_sg; + } + init_completion(&comp); + desc->callback = phytium_dma_callback; + desc->callback_param = ∁ + + dmaengine_submit(desc); + dma_async_issue_pending(dma_chan); + + timeout = wait_for_completion_timeout(&comp, 2 * HZ); + if (timeout == 0) { + DRM_DEV_ERROR(drm_dev->dev, "wait for dma callback timeout\n"); + ret = -EIO; + } + dma_sync_sg_for_cpu(dma_chan->device->dev, st.sgl, st.nents, dir); + +failed_prep_slave_sg: + dma_unmap_sg(dma_chan->device->dev, st.sgl, st.nents, dir); +failed_dma_map_sg: + sg_free_table(&st); +failed_sg_alloc_table: + return ret; +} + +int phytium_gem_suspend(struct drm_device *drm_dev) +{ + struct phytium_display_private *priv = drm_dev->dev_private; + struct phytium_gem_object *phytium_gem_obj = NULL; + int ret = 0; + + list_for_each_entry(phytium_gem_obj, &priv->gem_list_head, list) { + if (phytium_gem_obj->memory_type != MEMORY_TYPE_VRAM) + continue; + + phytium_gem_obj->vaddr_save = vmalloc(phytium_gem_obj->size); + if (!phytium_gem_obj->vaddr_save) + goto malloc_failed; + + if (priv->dma_inited) + ret = phytium_dma_transfer(drm_dev, 1, phytium_gem_obj->vaddr_save, + phytium_gem_obj->iova, phytium_gem_obj->size); + + if ((!priv->dma_inited) || ret) + memcpy(phytium_gem_obj->vaddr_save, phytium_gem_obj->vaddr, + phytium_gem_obj->size); + } + + return 0; +malloc_failed: + list_for_each_entry(phytium_gem_obj, &priv->gem_list_head, list) { + if (phytium_gem_obj->memory_type != MEMORY_TYPE_VRAM) + continue; + + if (phytium_gem_obj->vaddr_save) { + vfree(phytium_gem_obj->vaddr_save); + phytium_gem_obj->vaddr_save = NULL; + } + } + return -ENOMEM; +} + +void phytium_gem_resume(struct drm_device *drm_dev) +{ + struct phytium_display_private *priv = drm_dev->dev_private; + struct phytium_gem_object *phytium_gem_obj = NULL; + + list_for_each_entry(phytium_gem_obj, &priv->gem_list_head, list) { + if (phytium_gem_obj->memory_type != MEMORY_TYPE_VRAM) + continue; + + memcpy(phytium_gem_obj->vaddr, phytium_gem_obj->vaddr_save, phytium_gem_obj->size); + vfree(phytium_gem_obj->vaddr_save); + phytium_gem_obj->vaddr_save = NULL; + } +} + +void phytium_gem_free_object(struct drm_gem_object *obj) +{ + struct phytium_gem_object *phytium_gem_obj = to_phytium_gem_obj(obj); + struct drm_device *dev = obj->dev; + struct phytium_display_private *priv = dev->dev_private; + uint64_t size = phytium_gem_obj->size; + + DRM_DEBUG_KMS("free phytium_gem_obj iova:0x%pa size:0x%lx\n", + &phytium_gem_obj->iova, phytium_gem_obj->size); + if (phytium_gem_obj->vaddr) { + if (phytium_gem_obj->memory_type == MEMORY_TYPE_VRAM) { + phytium_memory_pool_free(priv, phytium_gem_obj->vaddr, size); + priv->mem_state[PHYTIUM_MEM_VRAM_ALLOC] -= size; + } else if (phytium_gem_obj->memory_type == MEMORY_TYPE_SYSTEM_CARVEOUT) { + dma_unmap_page(dev->dev, phytium_gem_obj->iova, size, DMA_TO_DEVICE); + phytium_memory_pool_free(priv, phytium_gem_obj->vaddr, size); + priv->mem_state[PHYTIUM_MEM_SYSTEM_CARVEOUT_ALLOC] -= size; + } else if (phytium_gem_obj->memory_type == MEMORY_TYPE_SYSTEM_UNIFIED) { + dma_free_attrs(dev->dev, size, phytium_gem_obj->vaddr, + phytium_gem_obj->iova, 0); + priv->mem_state[PHYTIUM_MEM_SYSTEM_UNIFIED_ALLOC] -= size; + } + list_del(&phytium_gem_obj->list); + } else if (obj->import_attach) + drm_prime_gem_destroy(obj, phytium_gem_obj->sgt); + drm_gem_object_release(obj); + kfree(phytium_gem_obj); +} + +int phytium_gem_mmap_obj(struct drm_gem_object *obj, struct vm_area_struct *vma) +{ + int ret = 0; + struct phytium_gem_object *phytium_gem_obj = to_phytium_gem_obj(obj); + unsigned long pfn = PHYS_PFN(phytium_gem_obj->phys_addr); + /* + * Clear the VM_PFNMAP flag that was set by drm_gem_mmap(), and set the + * vm_pgoff (used as a fake buffer offset by DRM) to 0 as we want to map + * the whole buffer. + */ + vma->vm_flags &= ~VM_PFNMAP; + vma->vm_pgoff = 0; + vma->vm_page_prot = vm_get_page_prot(vma->vm_flags); + + if (phytium_gem_obj->memory_type == MEMORY_TYPE_VRAM) { + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + ret = remap_pfn_range(vma, vma->vm_start, pfn, + vma->vm_end - vma->vm_start, vma->vm_page_prot); + } else if (phytium_gem_obj->memory_type == MEMORY_TYPE_SYSTEM_CARVEOUT) { + ret = remap_pfn_range(vma, vma->vm_start, pfn, + vma->vm_end - vma->vm_start, vma->vm_page_prot); + } else if (phytium_gem_obj->memory_type == MEMORY_TYPE_SYSTEM_UNIFIED) { + ret = dma_mmap_attrs(obj->dev->dev, vma, phytium_gem_obj->vaddr, + phytium_gem_obj->iova, vma->vm_end - vma->vm_start, 0); + } + if (ret) + drm_gem_vm_close(vma); + + return ret; +} + +int phytium_gem_mmap(struct file *filp, struct vm_area_struct *vma) +{ + int ret = 0; + + ret = drm_gem_mmap(filp, vma); + if (ret < 0) + return ret; + + return phytium_gem_mmap_obj(vma->vm_private_data, vma); +} + +int phytium_gem_dumb_destroy(struct drm_file *file, struct drm_device *dev, uint32_t handle) +{ + return drm_gem_dumb_destroy(file, dev, handle); +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)) +static const struct vm_operations_struct phytium_vm_ops = { + .open = drm_gem_vm_open, + .close = drm_gem_vm_close, +}; + +static const struct drm_gem_object_funcs phytium_drm_gem_object_funcs = { + .free = phytium_gem_free_object, + .get_sg_table = phytium_gem_prime_get_sg_table, + .vmap = phytium_gem_prime_vmap, + .vunmap = phytium_gem_prime_vunmap, + .vm_ops = &phytium_vm_ops, +}; +#endif + +struct phytium_gem_object *phytium_gem_create_object(struct drm_device *dev, unsigned long size) +{ + struct phytium_gem_object *phytium_gem_obj = NULL; + struct phytium_display_private *priv = dev->dev_private; + struct page *page = NULL; + int ret = 0; + + phytium_gem_obj = kzalloc(sizeof(*phytium_gem_obj), GFP_KERNEL); + if (!phytium_gem_obj) { + DRM_ERROR("failed to allocate phytium_gem_obj\n"); + ret = -ENOMEM; + goto error; + } + + ret = drm_gem_object_init(dev, &phytium_gem_obj->base, size); + if (ret) { + DRM_ERROR("failed to initialize drm gem object: %d\n", ret); + goto failed_object_init; + } + + if (priv->support_memory_type & MEMORY_TYPE_VRAM) { + ret = phytium_memory_pool_alloc(priv, &phytium_gem_obj->vaddr, + &phytium_gem_obj->phys_addr, size); + if (ret) { + DRM_ERROR("fail to allocate vram buffer with size %lx\n", size); + goto failed_dma_alloc; + } + phytium_gem_obj->iova = phytium_gem_obj->phys_addr; + phytium_gem_obj->memory_type = MEMORY_TYPE_VRAM; + priv->mem_state[PHYTIUM_MEM_VRAM_ALLOC] += size; + } else if (priv->support_memory_type & MEMORY_TYPE_SYSTEM_CARVEOUT) { + ret = phytium_memory_pool_alloc(priv, &phytium_gem_obj->vaddr, + &phytium_gem_obj->phys_addr, size); + if (ret) { + DRM_ERROR("fail to allocate carveout memory with size %lx\n", size); + goto failed_dma_alloc; + } + page = phys_to_page(phytium_gem_obj->phys_addr); + phytium_gem_obj->iova = dma_map_page(dev->dev, page, 0, size, DMA_TO_DEVICE); + if (dma_mapping_error(dev->dev, phytium_gem_obj->iova)) { + DRM_ERROR("fail to dma map carveout memory with size %lx\n", size); + phytium_memory_pool_free(priv, phytium_gem_obj->vaddr, size); + ret = -ENOMEM; + goto failed_dma_alloc; + } + phytium_gem_obj->memory_type = MEMORY_TYPE_SYSTEM_CARVEOUT; + priv->mem_state[PHYTIUM_MEM_SYSTEM_CARVEOUT_ALLOC] += size; + } else if (priv->support_memory_type & MEMORY_TYPE_SYSTEM_UNIFIED) { + phytium_gem_obj->vaddr = dma_alloc_attrs(dev->dev, size, &phytium_gem_obj->iova, + GFP_KERNEL, 0); + if (!phytium_gem_obj->vaddr) { + DRM_ERROR("fail to allocate unified buffer with size %lx\n", size); + ret = -ENOMEM; + goto failed_dma_alloc; + } + phytium_gem_obj->memory_type = MEMORY_TYPE_SYSTEM_UNIFIED; + priv->mem_state[PHYTIUM_MEM_SYSTEM_UNIFIED_ALLOC] += size; + } else { + DRM_ERROR("fail to allocate buffer with size %lx\n", size); + ret = -ENOMEM; + goto failed_dma_alloc; + } + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)) + phytium_gem_obj->base.funcs = &phytium_drm_gem_object_funcs; +#endif + + phytium_gem_obj->size = size; + list_add_tail(&phytium_gem_obj->list, &priv->gem_list_head); + DRM_DEBUG_KMS("phytium_gem_obj iova:0x%pa size:0x%lx\n", + &phytium_gem_obj->iova, phytium_gem_obj->size); + return phytium_gem_obj; + +failed_dma_alloc: +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)) + drm_gem_object_put(&phytium_gem_obj->base); +#else + drm_gem_object_unreference_unlocked(&phytium_gem_obj->base); +#endif + + return ERR_PTR(ret); +failed_object_init: + kfree(phytium_gem_obj); +error: + return ERR_PTR(ret); +} + +int phytium_gem_dumb_create(struct drm_file *file, struct drm_device *dev, + struct drm_mode_create_dumb *args) +{ + int size = 0; + struct phytium_gem_object *phytium_gem_obj = NULL; + int ret = 0; + + args->pitch = ALIGN(args->width*DIV_ROUND_UP(args->bpp, 8), 128); + args->size = args->pitch * args->height; + size = PAGE_ALIGN(args->size); + phytium_gem_obj = phytium_gem_create_object(dev, size); + if (IS_ERR(phytium_gem_obj)) + return PTR_ERR(phytium_gem_obj); + ret = drm_gem_handle_create(file, &phytium_gem_obj->base, &args->handle); + if (ret) { + DRM_ERROR("failed to drm_gem_handle_create\n"); + goto failed_gem_handle; + } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)) + drm_gem_object_put(&phytium_gem_obj->base); +#else + drm_gem_object_unreference_unlocked(&phytium_gem_obj->base); +#endif + + return 0; +failed_gem_handle: + phytium_gem_free_object(&phytium_gem_obj->base); + return ret; +} diff --git a/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_gem.h b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_gem.h new file mode 100644 index 000000000..cd34f4a45 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_gem.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Phytium display drm driver + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#ifndef __PHYTIUM_GEM_H__ +#define __PHYTIUM_GEM_H__ + +#include + +struct phytium_gem_object { + struct drm_gem_object base; + phys_addr_t phys_addr; + dma_addr_t iova; + void *vaddr; + unsigned long size; + struct sg_table *sgt; + char memory_type; + char reserve[3]; + struct list_head list; + void *vaddr_save; +}; + +#define to_phytium_gem_obj(obj) container_of(obj, struct phytium_gem_object, base) + +int phytium_memory_pool_init(struct device *dev, struct phytium_display_private *priv); +void phytium_memory_pool_fini(struct device *dev, struct phytium_display_private *priv); +int phytium_gem_mmap_obj(struct drm_gem_object *obj, struct vm_area_struct *vma); +int phytium_gem_mmap(struct file *filp, struct vm_area_struct *vma); +void phytium_gem_free_object(struct drm_gem_object *obj); +struct sg_table *phytium_gem_prime_get_sg_table(struct drm_gem_object *obj); +struct drm_gem_object *phytium_gem_prime_import_sg_table(struct drm_device *dev, + struct dma_buf_attachment *attach, struct sg_table *sgt); +void phytium_gem_free_object(struct drm_gem_object *obj); +int phytium_gem_dumb_destroy(struct drm_file *file, struct drm_device *dev, unsigned int handle); +struct phytium_gem_object *phytium_gem_create_object(struct drm_device *dev, unsigned long size); +int phytium_gem_dumb_create(struct drm_file *file, struct drm_device *dev, + struct drm_mode_create_dumb *args); +void *phytium_gem_prime_vmap(struct drm_gem_object *obj); +void phytium_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr); +int phytium_gem_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma); +int phytium_gem_suspend(struct drm_device *drm_dev); +void phytium_gem_resume(struct drm_device *drm_dev); +#endif /* __PHYTIUM_GEM_H__ */ diff --git a/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_panel.c b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_panel.c new file mode 100644 index 000000000..16783b24a --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_panel.c @@ -0,0 +1,420 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Phytium display drm driver + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include "phytium_display_drv.h" +#include "phytium_dp.h" +#include "phytium_panel.h" + +static int +phytium_dp_aux_set_backlight(struct phytium_panel *panel, unsigned int level) +{ + struct phytium_dp_device *phytium_dp = panel_to_dp_device(panel); + unsigned char vals[2] = { 0x0 }; + + vals[0] = level; + if (phytium_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_BYTE_COUNT) { + vals[0] = (level & 0xFF00) >> 8; + vals[1] = (level & 0xFF); + } + + if (drm_dp_dpcd_write(&phytium_dp->aux, DP_EDP_BACKLIGHT_BRIGHTNESS_MSB, + vals, sizeof(vals)) < 0) { + DRM_DEBUG_KMS("Failed to write aux backlight level\n"); + return -EIO; + } + + return 0; +} + +static unsigned int phytium_dp_aux_get_backlight(struct phytium_panel *panel) +{ + unsigned char read_val[2] = { 0x0 }; + unsigned char level = 0; + struct phytium_dp_device *phytium_dp = panel_to_dp_device(panel); + + if (drm_dp_dpcd_read(&phytium_dp->aux, DP_EDP_BACKLIGHT_BRIGHTNESS_MSB, + &read_val, sizeof(read_val)) < 0) { + DRM_DEBUG_KMS("Failed to read DPCD register 0x%x\n", + DP_EDP_BACKLIGHT_BRIGHTNESS_MSB); + return 0; + } + + level = read_val[0]; + if (phytium_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_BYTE_COUNT) + level = (read_val[0] << 8 | read_val[1]); + + return level; +} + +static void set_aux_backlight_enable(struct phytium_panel *panel, bool enable) +{ + u8 reg_val = 0; + struct phytium_dp_device *phytium_dp = panel_to_dp_device(panel); + + if (!(phytium_dp->edp_dpcd[1] & DP_EDP_BACKLIGHT_AUX_ENABLE_CAP)) + return; + + if (drm_dp_dpcd_readb(&phytium_dp->aux, DP_EDP_DISPLAY_CONTROL_REGISTER, + ®_val) < 0) { + DRM_DEBUG_KMS("Failed to read DPCD register 0x%x\n", + DP_EDP_DISPLAY_CONTROL_REGISTER); + return; + } + + if (enable) + reg_val |= DP_EDP_BACKLIGHT_ENABLE; + else + reg_val &= ~(DP_EDP_BACKLIGHT_ENABLE); + + if (drm_dp_dpcd_writeb(&phytium_dp->aux, DP_EDP_DISPLAY_CONTROL_REGISTER, + reg_val) != 1) { + DRM_DEBUG_KMS("Failed to %s aux backlight\n", + enable ? "enable" : "disable"); + } +} + +static void phytium_dp_aux_enable_backlight(struct phytium_panel *panel) +{ + unsigned char dpcd_buf, new_dpcd_buf, edp_backlight_mode; + struct phytium_dp_device *phytium_dp = panel_to_dp_device(panel); + + if (drm_dp_dpcd_readb(&phytium_dp->aux, + DP_EDP_BACKLIGHT_MODE_SET_REGISTER, &dpcd_buf) != 1) { + DRM_DEBUG_KMS("Failed to read DPCD register 0x%x\n", + DP_EDP_BACKLIGHT_MODE_SET_REGISTER); + return; + } + + new_dpcd_buf = dpcd_buf; + edp_backlight_mode = dpcd_buf & DP_EDP_BACKLIGHT_CONTROL_MODE_MASK; + + switch (edp_backlight_mode) { + case DP_EDP_BACKLIGHT_CONTROL_MODE_PWM: + case DP_EDP_BACKLIGHT_CONTROL_MODE_PRESET: + case DP_EDP_BACKLIGHT_CONTROL_MODE_PRODUCT: + new_dpcd_buf &= ~DP_EDP_BACKLIGHT_CONTROL_MODE_MASK; + new_dpcd_buf |= DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD; + break; + + /* Do nothing when it is already DPCD mode */ + case DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD: + default: + break; + } + + if (new_dpcd_buf != dpcd_buf) { + if (drm_dp_dpcd_writeb(&phytium_dp->aux, + DP_EDP_BACKLIGHT_MODE_SET_REGISTER, new_dpcd_buf) < 0) { + DRM_DEBUG_KMS("Failed to write aux backlight mode\n"); + } + } + + set_aux_backlight_enable(panel, true); + phytium_dp_aux_set_backlight(panel, panel->level); +} + +static void phytium_dp_aux_disable_backlight(struct phytium_panel *panel) +{ + set_aux_backlight_enable(panel, false); +} + +static void phytium_dp_aux_setup_backlight(struct phytium_panel *panel) +{ + struct phytium_dp_device *phytium_dp = panel_to_dp_device(panel); + + if (phytium_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_BYTE_COUNT) + phytium_dp->panel.max = 0xFFFF; + else + phytium_dp->panel.max = 0xFF; + + phytium_dp->panel.min = 0; + phytium_dp->panel.level = phytium_dp_aux_get_backlight(panel); + phytium_dp->panel.backlight_enabled = (phytium_dp->panel.level != 0); +} + +static void phytium_dp_hw_poweron_panel(struct phytium_panel *panel) +{ + struct phytium_dp_device *phytium_dp = panel_to_dp_device(panel); + + phytium_dp->funcs->dp_hw_poweron_panel(phytium_dp); +} + +static void phytium_dp_hw_poweroff_panel(struct phytium_panel *panel) +{ + struct phytium_dp_device *phytium_dp = panel_to_dp_device(panel); + + phytium_dp->funcs->dp_hw_poweroff_panel(phytium_dp); +} + +static int +phytium_dp_hw_set_backlight(struct phytium_panel *panel, uint32_t level) +{ + int ret; + struct phytium_dp_device *phytium_dp = panel_to_dp_device(panel); + + ret = phytium_dp->funcs->dp_hw_set_backlight(phytium_dp, level); + + return ret; +} + +static uint32_t phytium_dp_hw_get_backlight(struct phytium_panel *panel) +{ + uint32_t ret; + struct phytium_dp_device *phytium_dp = panel_to_dp_device(panel); + + ret = phytium_dp->funcs->dp_hw_get_backlight(phytium_dp); + + return ret; +} + +static void phytium_dp_hw_enable_backlight(struct phytium_panel *panel) +{ + struct phytium_dp_device *phytium_dp = panel_to_dp_device(panel); + + phytium_dp->funcs->dp_hw_set_backlight(phytium_dp, phytium_dp->panel.level); + phytium_dp->funcs->dp_hw_enable_backlight(phytium_dp); +} + +static void phytium_dp_hw_disable_backlight(struct phytium_panel *panel) +{ + struct phytium_dp_device *phytium_dp = panel_to_dp_device(panel); + + phytium_dp->funcs->dp_hw_disable_backlight(phytium_dp); +} + +static void phytium_dp_hw_setup_backlight(struct phytium_panel *panel) +{ + struct drm_device *dev = panel->dev; + struct phytium_display_private *priv = dev->dev_private; + + panel->max = priv->info.backlight_max; + panel->min = 0; + panel->level = phytium_dp_hw_get_backlight(panel); +} + +void phytium_dp_panel_init_backlight_funcs(struct phytium_dp_device *phytium_dp) +{ + if (phytium_dp->edp_dpcd[1] & DP_EDP_TCON_BACKLIGHT_ADJUSTMENT_CAP && + (phytium_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_AUX_SET_CAP) && + !(phytium_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_PWM_PIN_CAP)) { + DRM_DEBUG_KMS("AUX Backlight Control Supported!\n"); + phytium_dp->panel.setup_backlight = phytium_dp_aux_setup_backlight; + phytium_dp->panel.enable_backlight = phytium_dp_aux_enable_backlight; + phytium_dp->panel.disable_backlight = phytium_dp_aux_disable_backlight; + phytium_dp->panel.set_backlight = phytium_dp_aux_set_backlight; + phytium_dp->panel.get_backlight = phytium_dp_aux_get_backlight; + } else { + DRM_DEBUG_KMS("SE Backlight Control Supported!\n"); + phytium_dp->panel.setup_backlight = phytium_dp_hw_setup_backlight; + phytium_dp->panel.enable_backlight = phytium_dp_hw_enable_backlight; + phytium_dp->panel.disable_backlight = phytium_dp_hw_disable_backlight; + phytium_dp->panel.set_backlight = phytium_dp_hw_set_backlight; + phytium_dp->panel.get_backlight = phytium_dp_hw_get_backlight; + } + phytium_dp->panel.poweron = phytium_dp_hw_poweron_panel; + phytium_dp->panel.poweroff = phytium_dp_hw_poweroff_panel; + mutex_init(&phytium_dp->panel.panel_lock); + phytium_dp->panel.dev = phytium_dp->dev; + + /* Upper limits from eDP 1.3 spec */ + phytium_dp->panel.panel_power_up_delay = 210; /* t1_t3 */ + phytium_dp->panel.backlight_on_delay = 50; /* t7 */ + phytium_dp->panel.backlight_off_delay = 50; + phytium_dp->panel.panel_power_down_delay = 0; /* t10 */ + phytium_dp->panel.panel_power_cycle_delay = 510; /* t11 + t12 */ +} + +void phytium_dp_panel_release_backlight_funcs(struct phytium_dp_device *phytium_dp) +{ + phytium_dp->panel.setup_backlight = NULL; + phytium_dp->panel.enable_backlight = NULL; + phytium_dp->panel.disable_backlight = NULL; + phytium_dp->panel.set_backlight = NULL; + phytium_dp->panel.get_backlight = NULL; + phytium_dp->panel.poweron = NULL; + phytium_dp->panel.poweroff = NULL; +} + +void phytium_panel_enable_backlight(struct phytium_panel *panel) +{ + + if (panel->enable_backlight) { + mutex_lock(&panel->panel_lock); + msleep(panel->backlight_on_delay); + panel->enable_backlight(panel); + panel->backlight_enabled = true; + mutex_unlock(&panel->panel_lock); + } +} + +void phytium_panel_disable_backlight(struct phytium_panel *panel) +{ + if (panel->disable_backlight) { + mutex_lock(&panel->panel_lock); + panel->disable_backlight(panel); + panel->backlight_enabled = false; + msleep(panel->backlight_off_delay); + mutex_unlock(&panel->panel_lock); + } +} + +void phytium_panel_poweron(struct phytium_panel *panel) +{ + if (panel->poweron) { + mutex_lock(&panel->panel_lock); + panel->poweron(panel); + panel->power_enabled = true; + msleep(panel->panel_power_up_delay); + mutex_unlock(&panel->panel_lock); + } +} + +void phytium_panel_poweroff(struct phytium_panel *panel) +{ + if (panel->poweroff) { + mutex_lock(&panel->panel_lock); + msleep(panel->panel_power_down_delay); + panel->poweroff(panel); + panel->power_enabled = false; + mutex_unlock(&panel->panel_lock); + } +} + +static uint32_t phytium_scale(uint32_t source_val, + uint32_t source_min, uint32_t source_max, + uint32_t target_min, uint32_t target_max) +{ + uint64_t target_val; + + WARN_ON(source_min > source_max); + WARN_ON(target_min > target_max); + + /* defensive */ + source_val = clamp(source_val, source_min, source_max); + + /* avoid overflows */ + target_val = mul_u32_u32(source_val - source_min, target_max - target_min); + target_val = DIV_ROUND_CLOSEST_ULL(target_val, source_max - source_min); + target_val += target_min; + + return target_val; +} + +static inline uint32_t +phytium_scale_hw_to_user(struct phytium_panel *panel, uint32_t hw_level, uint32_t user_max) +{ + return phytium_scale(hw_level, panel->min, panel->max, + 0, user_max); +} + +static inline uint32_t +phytium_scale_user_to_hw(struct phytium_panel *panel, u32 user_level, u32 user_max) +{ + return phytium_scale(user_level, 0, user_max, + panel->min, panel->max); +} + +static int phytium_backlight_device_update_status(struct backlight_device *bd) +{ + struct phytium_panel *panel = bl_get_data(bd); + struct drm_device *dev = panel->dev; + uint32_t hw_level = 0; + int ret = 0; + + DRM_DEBUG_KMS("updating phytium_backlight, brightness=%d/%d\n", + bd->props.brightness, bd->props.max_brightness); + drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); + hw_level = phytium_scale_user_to_hw(panel, bd->props.brightness, bd->props.max_brightness); + + if ((panel->set_backlight) && (panel->backlight_enabled)) { + mutex_lock(&panel->panel_lock); + ret = panel->set_backlight(panel, hw_level); + panel->level = hw_level; + mutex_unlock(&panel->panel_lock); + } + drm_modeset_unlock(&dev->mode_config.connection_mutex); + + return ret; +} + +static int phytium_backlight_device_get_brightness(struct backlight_device *bd) +{ + struct phytium_panel *panel = bl_get_data(bd); + struct drm_device *dev = panel->dev; + uint32_t hw_level = 0; + int ret; + + drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); + if (panel->get_backlight && panel->backlight_enabled) { + mutex_lock(&panel->panel_lock); + hw_level = panel->get_backlight(panel); + panel->level = hw_level; + mutex_unlock(&panel->panel_lock); + } + drm_modeset_unlock(&dev->mode_config.connection_mutex); + ret = phytium_scale_hw_to_user(panel, hw_level, bd->props.max_brightness); + DRM_DEBUG_KMS("get phytium_backlight, brightness=%d/%d\n", + ret, bd->props.max_brightness); + + return ret; +} + +static const struct backlight_ops phytium_backlight_device_ops = { + .update_status = phytium_backlight_device_update_status, + .get_brightness = phytium_backlight_device_get_brightness, +}; + +int phytium_edp_backlight_device_register(struct phytium_dp_device *phytium_dp) +{ + struct backlight_properties props; + char bl_name[16]; + + if (phytium_dp->panel.setup_backlight) { + mutex_lock(&phytium_dp->panel.panel_lock); + phytium_dp->panel.setup_backlight(&phytium_dp->panel); + mutex_unlock(&phytium_dp->panel.panel_lock); + } else { + return -EINVAL; + } + + memset(&props, 0, sizeof(props)); + props.max_brightness = PHYTIUM_MAX_BL_LEVEL; + props.type = BACKLIGHT_RAW; + props.brightness = phytium_scale_hw_to_user(&phytium_dp->panel, phytium_dp->panel.level, + props.max_brightness); + snprintf(bl_name, sizeof(bl_name), "phytium_bl%d", phytium_dp->port); + + phytium_dp->panel.bl_device = + backlight_device_register(bl_name, + phytium_dp->connector.kdev, + &phytium_dp->panel, + &phytium_backlight_device_ops, + &props); + + if (IS_ERR(phytium_dp->panel.bl_device)) { + DRM_ERROR("Failed to register backlight: %ld\n", + PTR_ERR(phytium_dp->panel.bl_device)); + phytium_dp->panel.bl_device = NULL; + return -ENODEV; + } + + DRM_DEBUG_KMS("Connector %s backlight sysfs interface registered\n", + phytium_dp->connector.name); + + return 0; +} + +void phytium_edp_backlight_device_unregister(struct phytium_dp_device *phytium_dp) +{ + if (phytium_dp->panel.bl_device) { + backlight_device_unregister(phytium_dp->panel.bl_device); + phytium_dp->panel.bl_device = NULL; + } +} diff --git a/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_panel.h b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_panel.h new file mode 100644 index 000000000..91760e26d --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_panel.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Phytium display drm driver + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#ifndef __PHYTIUM_PANEL_H__ +#define __PHYTIUM_PANEL_H__ +#include "phytium_dp.h" + +#define PHYTIUM_MAX_BL_LEVEL 0xFF + +struct phytium_panel { + struct drm_device *dev; + bool backlight_enabled; + bool power_enabled; + bool reserve1[2]; + unsigned int min; + unsigned int level; + unsigned int max; + struct backlight_device *bl_device; + void (*setup_backlight)(struct phytium_panel *panel); + uint32_t (*get_backlight)(struct phytium_panel *panel); + int (*set_backlight)(struct phytium_panel *panel, uint32_t level); + void (*disable_backlight)(struct phytium_panel *panel); + void (*enable_backlight)(struct phytium_panel *panel); + void (*poweron)(struct phytium_panel *panel); + void (*poweroff)(struct phytium_panel *panel); + struct mutex panel_lock; + uint32_t panel_power_up_delay; + uint32_t backlight_on_delay; + uint32_t backlight_off_delay; + uint32_t panel_power_down_delay; + uint32_t panel_power_cycle_delay; +}; + +void phytium_dp_panel_init_backlight_funcs(struct phytium_dp_device *phytium_dp); +void phytium_panel_release_backlight_funcs(struct phytium_dp_device *phytium_dp); +int phytium_edp_backlight_device_register(struct phytium_dp_device *phytium_dp); +void phytium_edp_backlight_device_unregister(struct phytium_dp_device *phytium_dp); +void phytium_panel_enable_backlight(struct phytium_panel *panel); +void phytium_panel_disable_backlight(struct phytium_panel *panel); +void phytium_panel_poweron(struct phytium_panel *panel); +void phytium_panel_poweroff(struct phytium_panel *panel); + +#endif /* __PHYTIUM_PANEL_H__ */ diff --git a/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_pci.c b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_pci.c new file mode 100644 index 000000000..22e4fd79b --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_pci.c @@ -0,0 +1,388 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Phytium display drm driver + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include "phytium_display_drv.h" +#include "phytium_pci.h" +#include "phytium_dp.h" +#include "phytium_gem.h" +#include "px210_dc.h" +#include "px210_dp.h" +#include "pe220x_dc.h" +#include "pe220x_dp.h" + +int dc_msi_enable; +module_param(dc_msi_enable, int, 0644); +MODULE_PARM_DESC(dc_msi_enable, "Enable DC msi interrupt (0-disabled; 1-enabled; default-0)"); + +void phytium_pci_vram_hw_init(struct phytium_display_private *priv) +{ + struct phytium_pci_private *pci_priv = to_pci_priv(priv); + + pci_priv->dc_hw_vram_init(priv, priv->pool_phys_addr, priv->pool_size); +} + +int phytium_pci_vram_init(struct pci_dev *pdev, struct phytium_display_private *priv) +{ + int ret = 0; + + priv->pool_phys_addr = pci_resource_start(pdev, 2); + priv->pool_size = pci_resource_len(pdev, 2); + if ((priv->pool_phys_addr != 0) && (priv->pool_size != 0)) { + priv->pool_virt_addr = devm_ioremap_wc(&pdev->dev, priv->pool_phys_addr, + priv->pool_size); + if (priv->pool_virt_addr == NULL) { + DRM_ERROR("pci vram ioremap fail, addr:0x%llx, size:0x%llx\n", + priv->pool_phys_addr, priv->pool_size); + ret = -EINVAL; + goto failed_ioremap; + } + ret = phytium_memory_pool_init(&pdev->dev, priv); + if (ret) + goto failed_init_memory_pool; + + priv->mem_state[PHYTIUM_MEM_VRAM_TOTAL] = priv->pool_size; + priv->support_memory_type = MEMORY_TYPE_VRAM; + priv->vram_hw_init = phytium_pci_vram_hw_init; + } else { + DRM_DEBUG_KMS("not support vram\n"); + priv->pool_virt_addr = NULL; + priv->mem_state[PHYTIUM_MEM_VRAM_TOTAL] = 0; + priv->support_memory_type = MEMORY_TYPE_SYSTEM_UNIFIED; + priv->vram_hw_init = NULL; + } + + return 0; + +failed_init_memory_pool: + devm_iounmap(&pdev->dev, priv->pool_virt_addr); +failed_ioremap: + return ret; +} + +void phytium_pci_vram_fini(struct pci_dev *pdev, struct phytium_display_private *priv) +{ + if (priv->support_memory_type == MEMORY_TYPE_VRAM) { + phytium_memory_pool_fini(&pdev->dev, priv); + devm_iounmap(&pdev->dev, priv->pool_virt_addr); + } +} + +static bool phytium_pci_dma_chan_filter(struct dma_chan *chan, void *param) +{ + struct phytium_dma_slave *s = param; + + if (s->dma_dev != chan->device->dev) + return false; + + if (s->chan_id == chan->chan_id) + return true; + else + return false; +} + +int phytium_pci_dma_init(struct phytium_display_private *priv) +{ + struct pci_dev *dma_dev, *gpu_dev; + struct drm_device *drm_dev = priv->dev; + dma_cap_mask_t mask; + struct phytium_dma_slave s; + int ret = 0; + u16 cmd; + + /* check px210 gpu enable */ + gpu_dev = pci_get_device(PCI_VENDOR_ID_PHYTIUM, 0xdc20, NULL); + if (!gpu_dev) { + DRM_INFO("failed to get gpu_dev\n"); + ret = -ENODEV; + goto failed; + } + + pci_read_config_word(gpu_dev, PCI_COMMAND, &cmd); + if (!(cmd & PCI_COMMAND_MASTER)) { + DRM_INFO("gpu_dev master is disabled\n"); + ret = -ENODEV; + goto failed; + } + + dma_dev = pci_get_device(PCI_VENDOR_ID_PHYTIUM, 0xdc3c, NULL); + if (!dma_dev) { + DRM_INFO("failed to get dma_dev\n"); + ret = -ENODEV; + goto failed; + } + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + s.dma_dev = &dma_dev->dev; + s.chan_id = 2; + priv->dma_chan = dma_request_channel(mask, phytium_pci_dma_chan_filter, &s); + if (!priv->dma_chan) { + DRM_DEV_ERROR(drm_dev->dev, "failed to request dma chan\n"); + ret = -EBUSY; + goto failed; + } + priv->dma_inited = 1; + +failed: + return ret; +} + +void phytium_pci_dma_fini(struct phytium_display_private *priv) +{ + if (priv->dma_inited) + dma_release_channel(priv->dma_chan); + priv->dma_inited = 0; + priv->dma_chan = NULL; +} + +static struct phytium_display_private* +phytium_pci_private_init(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + struct drm_device *dev = pci_get_drvdata(pdev); + struct phytium_display_private *priv = NULL; + struct phytium_pci_private *pci_priv = NULL; + struct phytium_device_info *phytium_info = (struct phytium_device_info *)ent->driver_data; + int i = 0; + resource_size_t io_addr, io_size; + + pci_priv = devm_kzalloc(&pdev->dev, sizeof(*pci_priv), GFP_KERNEL); + if (!pci_priv) { + DRM_ERROR("no memory to allocate for drm_display_private\n"); + goto failed_malloc_priv; + } + + memset(pci_priv, 0, sizeof(*pci_priv)); + priv = &pci_priv->base; + phytium_display_private_init(priv, dev); + + memcpy(&(priv->info), phytium_info, sizeof(struct phytium_device_info)); + DRM_DEBUG_KMS("priv->info.num_pipes :%d\n", priv->info.num_pipes); + priv->info.pipe_mask = ((pdev->subsystem_device >> PIPE_MASK_SHIFT) & PIPE_MASK_MASK); + priv->info.edp_mask = ((pdev->subsystem_device >> EDP_MASK_SHIFT) & EDP_MASK_MASK); + priv->info.num_pipes = 0; + for_each_pipe_masked(priv, i) + priv->info.num_pipes++; + if (priv->info.num_pipes == 0) { + DRM_ERROR("num_pipes is zero, so exit init\n"); + goto failed_init_numpipe; + } + + io_addr = pci_resource_start(pdev, 0); + io_size = pci_resource_len(pdev, 0); + priv->regs = ioremap(io_addr, io_size); + if (priv->regs == NULL) { + DRM_ERROR("pci bar0 ioremap fail, addr:0x%llx, size:0x%llx\n", io_addr, io_size); + goto failed_ioremap; + } + + priv->irq = pdev->irq; + if (IS_PX210(priv)) { + pci_priv->dc_hw_vram_init = px210_dc_hw_vram_init; + priv->dc_hw_clear_msi_irq = px210_dc_hw_clear_msi_irq; + priv->dc_hw_fb_format_check = px210_dc_hw_fb_format_check; + } else if (IS_PE220X(priv)) { + pci_priv->dc_hw_vram_init = pe220x_dc_hw_vram_init; + priv->dc_hw_clear_msi_irq = NULL; + priv->dc_hw_fb_format_check = pe220x_dc_hw_fb_format_check; + } + + return priv; + +failed_ioremap: +failed_init_numpipe: + devm_kfree(&pdev->dev, pci_priv); +failed_malloc_priv: + return NULL; +} + +static void +phytium_pci_private_fini(struct pci_dev *pdev, struct phytium_display_private *priv) +{ + struct phytium_pci_private *pci_priv = to_pci_priv(priv); + + if (priv->regs) + iounmap(priv->regs); + + devm_kfree(&pdev->dev, pci_priv); +} + +static int phytium_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + struct phytium_display_private *priv = NULL; + struct drm_device *dev = NULL; + int ret = 0; + + dev = drm_dev_alloc(&phytium_display_drm_driver, &pdev->dev); + if (IS_ERR(dev)) { + DRM_ERROR("failed to allocate drm_device\n"); + return PTR_ERR(dev); + } + dev->pdev = pdev; + pci_set_drvdata(pdev, dev); + pci_set_master(pdev); + ret = pci_enable_device(pdev); + if (ret) { + DRM_ERROR("pci enbale device fail\n"); + goto failed_enable_device; + } + + if (dc_msi_enable) { + ret = pci_enable_msi(pdev); + if (ret) + DRM_ERROR("pci enbale msi fail\n"); + } + + dma_set_mask(&pdev->dev, DMA_BIT_MASK(40)); + + priv = phytium_pci_private_init(pdev, ent); + if (priv) + dev->dev_private = priv; + else + goto failed_pci_private_init; + + ret = phytium_pci_vram_init(pdev, priv); + if (ret) { + DRM_ERROR("failed to init pci vram\n"); + goto failed_pci_vram_init; + } + + ret = drm_dev_register(dev, 0); + if (ret) { + DRM_ERROR("failed to register drm dev\n"); + goto failed_register_drm; + } + + phytium_dp_hpd_irq_setup(dev, true); + + return 0; + +failed_register_drm: + phytium_pci_vram_fini(pdev, priv); +failed_pci_vram_init: + phytium_pci_private_fini(pdev, priv); +failed_pci_private_init: + if (pdev->msi_enabled) + pci_disable_msi(pdev); + pci_disable_device(pdev); +failed_enable_device: + pci_set_drvdata(pdev, NULL); + drm_dev_put(dev); + + return -1; +} + +static void phytium_pci_remove(struct pci_dev *pdev) +{ + struct drm_device *dev = pci_get_drvdata(pdev); + struct phytium_display_private *priv = dev->dev_private; + + phytium_dp_hpd_irq_setup(dev, false); + cancel_work_sync(&priv->hotplug_work); + drm_dev_unregister(dev); + phytium_pci_vram_fini(pdev, priv); + phytium_pci_private_fini(pdev, priv); + if (pdev->msi_enabled) + pci_disable_msi(pdev); + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); + drm_dev_put(dev); +} + +static void phytium_pci_shutdown(struct pci_dev *pdev) +{ + struct drm_device *dev = pci_get_drvdata(pdev); + struct phytium_display_private *priv = dev->dev_private; + + priv->display_shutdown(dev); +} + +static int phytium_pci_pm_suspend(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct drm_device *drm_dev = pci_get_drvdata(pdev); + struct phytium_display_private *priv = drm_dev->dev_private; + int ret = 0; + + if (IS_PX210(priv)) + phytium_pci_dma_init(priv); + + ret = priv->display_pm_suspend(drm_dev); + if (ret < 0) + goto out; + + pci_save_state(pdev); + pci_disable_device(pdev); + pci_set_power_state(pdev, PCI_D3hot); + udelay(200); + +out: + return ret; +} + +static int phytium_pci_pm_resume(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct drm_device *drm_dev = pci_get_drvdata(pdev); + struct phytium_display_private *priv = drm_dev->dev_private; + int ret = 0; + + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + ret = pci_enable_device(pdev); + if (ret) + return ret; + pci_set_master(pdev); + + ret = priv->display_pm_resume(drm_dev); + if (IS_PX210(priv)) + phytium_pci_dma_fini(priv); + + return ret; +} + +static const struct dev_pm_ops phytium_pci_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(phytium_pci_pm_suspend, phytium_pci_pm_resume) +}; + +static const struct phytium_device_info px210_info = { + .platform_mask = BIT(PHYTIUM_PLATFORM_PX210), + .total_pipes = 3, + .crtc_clock_max = PX210_DC_PIX_CLOCK_MAX, + .hdisplay_max = PX210_DC_HDISPLAY_MAX, + .vdisplay_max = PX210_DC_VDISPLAY_MAX, + .address_mask = PX210_DC_ADDRESS_MASK, + .backlight_max = PX210_DP_BACKLIGHT_MAX, +}; + +static const struct phytium_device_info pe220x_info = { + .platform_mask = BIT(PHYTIUM_PLATFORM_PE220X), + .total_pipes = 2, + .crtc_clock_max = PE220X_DC_PIX_CLOCK_MAX, + .hdisplay_max = PE220X_DC_HDISPLAY_MAX, + .vdisplay_max = PE220X_DC_VDISPLAY_MAX, + .address_mask = PE220X_DC_ADDRESS_MASK, + .backlight_max = PE220X_DP_BACKLIGHT_MAX, +}; + +static const struct pci_device_id phytium_display_pci_ids[] = { + { PCI_VDEVICE(PHYTIUM, 0xdc22), (kernel_ulong_t)&px210_info }, + { PCI_VDEVICE(PHYTIUM, 0xdc3e), (kernel_ulong_t)&pe220x_info }, + { /* End: all zeroes */ } +}; +MODULE_DEVICE_TABLE(pci, phytium_display_pci_ids); + +struct pci_driver phytium_pci_driver = { + .name = "phytium_display_pci", + .id_table = phytium_display_pci_ids, + .probe = phytium_pci_probe, + .remove = phytium_pci_remove, + .shutdown = phytium_pci_shutdown, + .driver.pm = &phytium_pci_pm_ops, +}; diff --git a/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_pci.h b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_pci.h new file mode 100644 index 000000000..ad116dfcb --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_pci.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Phytium display drm driver + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#ifndef __PHYTIUM_PCI_H__ +#define __PHYTIUM_PCI_H__ + +#include "phytium_display_drv.h" + +struct phytium_pci_private { + struct phytium_display_private base; + void (*dc_hw_vram_init)(struct phytium_display_private *priv, resource_size_t vram_addr, + resource_size_t vram_size); +}; + +struct phytium_dma_slave { + struct device *dma_dev; + u32 chan_id; +}; + +#define to_pci_priv(priv) container_of(priv, struct phytium_pci_private, base) + +extern struct pci_driver phytium_pci_driver; +#endif /* __PHYTIUM_PCI_H__ */ diff --git a/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_plane.c b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_plane.c new file mode 100644 index 000000000..950db0487 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_plane.c @@ -0,0 +1,683 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Phytium display drm driver + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include "phytium_display_drv.h" +#include "phytium_plane.h" +#include "phytium_fb.h" +#include "phytium_gem.h" +#include "phytium_crtc.h" +#include "px210_dc.h" +#include "pe220x_dc.h" +#include "phytium_reg.h" + +#define PHYTIUM_CURS_W_SIZE 32 +#define PHYTIUM_CURS_H_SIZE 32 + +void phytium_plane_destroy(struct drm_plane *plane) +{ + struct phytium_plane *phytium_plane = to_phytium_plane(plane); + + drm_plane_cleanup(plane); + kfree(phytium_plane); +} + +/** + * phytium_plane_atomic_get_property - fetch plane property value + * @plane: plane to fetch property for + * @state: state containing the property value + * @property: property to look up + * @val: pointer to write property value into + * + * The DRM core does not store shadow copies of properties for + * atomic-capable drivers. This entrypoint is used to fetch + * the current value of a driver-specific plane property. + */ +static int +phytium_plane_atomic_get_property(struct drm_plane *plane, + const struct drm_plane_state *state, + struct drm_property *property, + uint64_t *val) +{ + DRM_DEBUG_KMS("Unknown plane property [PROP:%d:%s]\n", property->base.id, property->name); + return -EINVAL; +} + +/** + * phytium_plane_atomic_set_property - set plane property value + * @plane: plane to set property for + * @state: state to update property value in + * @property: property to set + * @val: value to set property to + * + * Writes the specified property value for a plane into the provided atomic + * state object. + * + * Returns 0 on success, -EINVAL on unrecognized properties + */ +int +phytium_plane_atomic_set_property(struct drm_plane *plane, + struct drm_plane_state *state, + struct drm_property *property, + uint64_t val) +{ + DRM_DEBUG_KMS("Unknown plane property [PROP:%d:%s]\n", property->base.id, property->name); + return -EINVAL; +} + +struct drm_plane_state * +phytium_plane_atomic_duplicate_state(struct drm_plane *plane) +{ + struct drm_plane_state *state = NULL; + struct phytium_plane_state *phytium_state = NULL; + + phytium_state = kmemdup(plane->state, sizeof(*phytium_state), GFP_KERNEL); + + if (!phytium_state) + return NULL; + + state = &phytium_state->base; + if (state->fb) + drm_framebuffer_get(state->fb); + + state->fence = NULL; + state->commit = NULL; + + return state; +} + +void +phytium_plane_atomic_destroy_state(struct drm_plane *plane, struct drm_plane_state *state) +{ + struct phytium_plane_state *phytium_state = to_phytium_plane_state(state); + + __drm_atomic_helper_plane_destroy_state(state); + kfree(phytium_state); +} + +static bool phytium_plane_format_mod_supported(struct drm_plane *plane, + uint32_t format, uint64_t modifier) +{ + if (modifier == DRM_FORMAT_MOD_LINEAR) + return true; + + if (modifier == DRM_FORMAT_MOD_PHYTIUM_TILE_MODE3_FBCDC) { + switch (format) { + case DRM_FORMAT_ARGB2101010: + case DRM_FORMAT_ABGR2101010: + case DRM_FORMAT_RGBA1010102: + case DRM_FORMAT_BGRA1010102: + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_ABGR8888: + case DRM_FORMAT_RGBA8888: + case DRM_FORMAT_BGRA8888: + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_RGBX8888: + case DRM_FORMAT_BGRX8888: + return true; + default: + return false; + } + } + + return false; +} + +const struct drm_plane_funcs phytium_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = phytium_plane_destroy, + .reset = drm_atomic_helper_plane_reset, + .atomic_get_property = phytium_plane_atomic_get_property, + .atomic_set_property = phytium_plane_atomic_set_property, + .atomic_duplicate_state = phytium_plane_atomic_duplicate_state, + .atomic_destroy_state = phytium_plane_atomic_destroy_state, + .format_mod_supported = phytium_plane_format_mod_supported, +}; + +static int phytium_plane_prepare_fb(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct dma_buf *dma_buf; + struct dma_fence *fence; + + if (!state->fb) + return 0; + dma_buf = to_phytium_framebuffer(state->fb)->phytium_gem_obj[0]->base.dma_buf; + if (dma_buf) { + fence = dma_resv_get_excl_rcu(dma_buf->resv); + drm_atomic_set_fence_for_plane(state, fence); + } + + return 0; +} + +static int +phytium_plane_atomic_check(struct drm_plane *plane, struct drm_plane_state *state) +{ + struct drm_device *dev = plane->dev; + struct phytium_display_private *priv = dev->dev_private; + struct drm_framebuffer *fb = state->fb; + struct drm_crtc *crtc = state->crtc; + struct drm_crtc_state *crtc_state; + int src_x, src_y, src_w, src_h; + unsigned long base_offset; + struct phytium_crtc *phytium_crtc = to_phytium_crtc(crtc); + + if ((!fb) || (!crtc)) + return 0; + + crtc_state = drm_atomic_get_crtc_state(state->state, crtc); + if (IS_ERR(crtc_state)) + return PTR_ERR(crtc_state); + + if (plane->type == DRM_PLANE_TYPE_CURSOR) { + src_w = state->src_w >> 16; + src_h = state->src_h >> 16; + if (phytium_crtc->scale_enable) + return -EINVAL; + if ((src_w != PHYTIUM_CURS_W_SIZE) || (src_h != PHYTIUM_CURS_W_SIZE)) { + DRM_INFO("Invalid cursor size(%d, %d)\n", src_w, src_h); + return -EINVAL; + } + } else if (plane->type == DRM_PLANE_TYPE_PRIMARY) { + src_x = state->src_x >> 16; + src_y = state->src_y >> 16; + src_w = state->src_w >> 16; + src_h = state->src_h >> 16; + + base_offset = src_x * fb->format->cpp[0] + src_y*fb->pitches[0]; + if (base_offset & (priv->info.address_mask)) { + DRM_ERROR("fb base address is not aligned by 0x%lx byte\n", + priv->info.address_mask); + return -EINVAL; + } + + if (src_w != state->crtc_w || src_h != state->crtc_h) { + DRM_ERROR("scale not support: crtc_w(0x%x)/h(0x%x) src_w(0x%x)/h(0x%x)\n", + state->crtc_w, state->crtc_h, src_w, src_h); + return -EINVAL; + } + + if ((state->crtc_x < 0) || (state->crtc_y < 0)) { + DRM_ERROR("crtc_x(0x%x)/y(0x%x) of drm plane state is invalid\n", + state->crtc_x, state->crtc_y); + return -EINVAL; + } + + if ((state->crtc_x + state->crtc_w > crtc_state->adjusted_mode.hdisplay) + || (state->crtc_y + state->crtc_h > crtc_state->adjusted_mode.vdisplay)) { + DRM_ERROR("plane out of crtc region\n"); + return -EINVAL; + } + } + + return 0; +} + +static void phytium_dc_get_plane_parameter(struct drm_plane *plane) +{ + struct phytium_plane *phytium_plane = to_phytium_plane(plane); + struct drm_framebuffer *fb = plane->state->fb; + struct phytium_framebuffer *phytium_fb = to_phytium_framebuffer(fb); + struct phytium_gem_object *phytium_gem_obj = NULL; + int i, num_planes = 0; + const struct drm_format_info *info; + + info = drm_format_info(fb->format->format); + num_planes = info ? info->num_planes : 1; + + for (i = 0; i < num_planes; i++) { + phytium_gem_obj = phytium_fb->phytium_gem_obj[i]; + phytium_plane->iova[i] = phytium_gem_obj->iova + fb->offsets[i]; + phytium_plane->size[i] = phytium_gem_obj->size - fb->offsets[i]; + + if (fb->modifier == DRM_FORMAT_MOD_PHYTIUM_TILE_MODE0_FBCDC) + phytium_plane->tiling[i] = FRAMEBUFFER_TILE_MODE0; + else if (fb->modifier == DRM_FORMAT_MOD_PHYTIUM_TILE_MODE3_FBCDC) + phytium_plane->tiling[i] = FRAMEBUFFER_TILE_MODE3; + else if (fb->modifier == DRM_FORMAT_MOD_LINEAR) + phytium_plane->tiling[i] = FRAMEBUFFER_LINEAR; + else + phytium_plane->tiling[i] = FRAMEBUFFER_LINEAR; + + if (i == 0) { + switch (fb->format->format) { + case DRM_FORMAT_ARGB2101010: + case DRM_FORMAT_ABGR2101010: + case DRM_FORMAT_RGBA1010102: + case DRM_FORMAT_BGRA1010102: + phytium_plane->format = FRAMEBUFFER_FORMAT_ARGB2101010; + break; + + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_ABGR8888: + case DRM_FORMAT_RGBA8888: + case DRM_FORMAT_BGRA8888: + phytium_plane->format = FRAMEBUFFER_FORMAT_ARGB8888; + break; + + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_RGBX8888: + case DRM_FORMAT_BGRX8888: + phytium_plane->format = FRAMEBUFFER_FORMAT_XRGB8888; + break; + + case DRM_FORMAT_ARGB4444: + case DRM_FORMAT_ABGR4444: + case DRM_FORMAT_RGBA4444: + case DRM_FORMAT_BGRA4444: + phytium_plane->format = FRAMEBUFFER_FORMAT_ARGB4444; + break; + + case DRM_FORMAT_XRGB4444: + case DRM_FORMAT_XBGR4444: + case DRM_FORMAT_RGBX4444: + case DRM_FORMAT_BGRX4444: + phytium_plane->format = FRAMEBUFFER_FORMAT_XRGB4444; + break; + + case DRM_FORMAT_ARGB1555: + case DRM_FORMAT_ABGR1555: + case DRM_FORMAT_RGBA5551: + case DRM_FORMAT_BGRA5551: + phytium_plane->format = FRAMEBUFFER_FORMAT_ARGB1555; + break; + + case DRM_FORMAT_XRGB1555: + case DRM_FORMAT_XBGR1555: + case DRM_FORMAT_RGBX5551: + case DRM_FORMAT_BGRX5551: + phytium_plane->format = FRAMEBUFFER_FORMAT_XRGB1555; + break; + + case DRM_FORMAT_RGB565: + case DRM_FORMAT_BGR565: + phytium_plane->format = FRAMEBUFFER_FORMAT_RGB565; + break; + + case DRM_FORMAT_YUYV: + phytium_plane->format = FRAMEBUFFER_FORMAT_YUYV; + break; + + case DRM_FORMAT_UYVY: + phytium_plane->format = FRAMEBUFFER_FORMAT_UYVY; + break; + case DRM_FORMAT_NV16: + phytium_plane->format = FRAMEBUFFER_FORMAT_NV16; + break; + case DRM_FORMAT_NV12: + phytium_plane->format = FRAMEBUFFER_FORMAT_NV12; + break; + case DRM_FORMAT_NV21: + phytium_plane->format = FRAMEBUFFER_FORMAT_NV12; + break; + default: + DRM_ERROR("unsupported pixel format (format = %d)\n", + fb->format->format); + return; + } + + switch (fb->format->format) { + case DRM_FORMAT_ARGB2101010: + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_ARGB4444: + case DRM_FORMAT_XRGB4444: + case DRM_FORMAT_ARGB1555: + case DRM_FORMAT_XRGB1555: + case DRM_FORMAT_RGB565: + phytium_plane->swizzle = FRAMEBUFFER_SWIZZLE_ARGB; + phytium_plane->uv_swizzle = FRAMEBUFFER_UVSWIZZLE_DISABLE; + break; + + case DRM_FORMAT_ABGR2101010: + case DRM_FORMAT_ABGR8888: + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_ABGR4444: + case DRM_FORMAT_XBGR4444: + case DRM_FORMAT_ABGR1555: + case DRM_FORMAT_XBGR1555: + case DRM_FORMAT_BGR565: + phytium_plane->swizzle = FRAMEBUFFER_SWIZZLE_ABGR; + phytium_plane->uv_swizzle = FRAMEBUFFER_UVSWIZZLE_DISABLE; + break; + + case DRM_FORMAT_RGBA1010102: + case DRM_FORMAT_RGBA8888: + case DRM_FORMAT_RGBX8888: + case DRM_FORMAT_RGBA4444: + case DRM_FORMAT_RGBX4444: + case DRM_FORMAT_RGBA5551: + case DRM_FORMAT_RGBX5551: + phytium_plane->swizzle = FRAMEBUFFER_SWIZZLE_RGBA; + phytium_plane->uv_swizzle = FRAMEBUFFER_UVSWIZZLE_DISABLE; + break; + + case DRM_FORMAT_BGRA1010102: + case DRM_FORMAT_BGRA8888: + case DRM_FORMAT_BGRX8888: + case DRM_FORMAT_BGRA4444: + case DRM_FORMAT_BGRX4444: + case DRM_FORMAT_BGRA5551: + case DRM_FORMAT_BGRX5551: + phytium_plane->swizzle = FRAMEBUFFER_SWIZZLE_BGRA; + phytium_plane->uv_swizzle = FRAMEBUFFER_UVSWIZZLE_DISABLE; + break; + + case DRM_FORMAT_YUYV: + case DRM_FORMAT_UYVY: + case DRM_FORMAT_NV16: + case DRM_FORMAT_NV12: + phytium_plane->swizzle = FRAMEBUFFER_SWIZZLE_ARGB; + phytium_plane->uv_swizzle = FRAMEBUFFER_UVSWIZZLE_DISABLE; + break; + + default: + DRM_ERROR("unsupported pixel format (format = %d)\n", + fb->format->format); + return; + } + } + } +} + +static void phytium_dc_primary_plane_update(struct drm_plane *plane) +{ + struct drm_device *dev = plane->dev; + struct phytium_display_private *priv = dev->dev_private; + struct phytium_plane *phytium_plane = to_phytium_plane(plane); + struct drm_framebuffer *fb = plane->state->fb; + int phys_pipe = phytium_plane->phys_pipe; + int src_x, src_y, crtc_x, crtc_y, crtc_w, crtc_h; + unsigned long base_offset; + int config; + + src_x = plane->state->src_x >> 16; + src_y = plane->state->src_y >> 16; + crtc_x = plane->state->crtc_x; + crtc_y = plane->state->crtc_y; + crtc_w = plane->state->crtc_w; + crtc_h = plane->state->crtc_h; + + if (phytium_plane->dc_hw_update_dcreq) + phytium_plane->dc_hw_update_dcreq(plane); + phytium_plane->dc_hw_update_primary_hi_addr(plane); + + /* config dc */ + /* Y */ + base_offset = src_x * fb->format->cpp[0] + src_y*fb->pitches[0]; + phytium_writel_reg(priv, (phytium_plane->iova[0] + base_offset) & ADDRESS_MASK, + priv->dc_reg_base[phys_pipe], PHYTIUM_DC_FRAMEBUFFER_Y_ADDRESS); + phytium_writel_reg(priv, ALIGN(fb->pitches[0], 128), + priv->dc_reg_base[phys_pipe], PHYTIUM_DC_FRAMEBUFFER_Y_STRIDE); + + /* U */ + phytium_writel_reg(priv, phytium_plane->iova[1] & 0xffffffff, + priv->dc_reg_base[phys_pipe], PHYTIUM_DC_FRAMEBUFFER_U_ADDRESS); + phytium_writel_reg(priv, ALIGN(fb->pitches[1], 128), + priv->dc_reg_base[phys_pipe], PHYTIUM_DC_FRAMEBUFFER_U_STRIDE); + + /* V */ + phytium_writel_reg(priv, phytium_plane->iova[2] & 0xffffffff, + priv->dc_reg_base[phys_pipe], PHYTIUM_DC_FRAMEBUFFER_V_ADDRESS); + phytium_writel_reg(priv, ALIGN(fb->pitches[2], 128), + priv->dc_reg_base[phys_pipe], PHYTIUM_DC_FRAMEBUFFER_V_STRIDE); + + /* size */ + phytium_writel_reg(priv, (crtc_w & WIDTH_MASK) | ((crtc_h&HEIGHT_MASK) << HEIGHT_SHIFT), + priv->dc_reg_base[phys_pipe], PHYTIUM_DC_FRAMEBUFFER_SIZE); + /* config */ + config = phytium_readl_reg(priv, priv->dc_reg_base[phys_pipe], + PHYTIUM_DC_FRAMEBUFFER_CONFIG); + config &= ~(FRAMEBUFFER_FORMAT_MASK << FRAMEBUFFER_FORMAT_SHIFT); + config |= (phytium_plane->format << FRAMEBUFFER_FORMAT_SHIFT); + config &= ~(1 << FRAMEBUFFER_UVSWIZZLE_SHIFT); + config |= (phytium_plane->uv_swizzle << FRAMEBUFFER_UVSWIZZLE_SHIFT); + config &= ~(FRAMEBUFFER_SWIZZLE_MASK << FRAMEBUFFER_SWIZZLE_SHIFT); + config |= (phytium_plane->swizzle << FRAMEBUFFER_SWIZZLE_SHIFT); + config &= ~(FRAMEBUFFER_TILE_MODE_MASK << FRAMEBUFFER_TILE_MODE_SHIFT); + config |= (phytium_plane->tiling[0] << FRAMEBUFFER_TILE_MODE_SHIFT); + config &= (~FRAMEBUFFER_CLEAR); + phytium_writel_reg(priv, config, priv->dc_reg_base[phys_pipe], + PHYTIUM_DC_FRAMEBUFFER_CONFIG); +} + +static void phytium_dc_cursor_plane_update(struct drm_plane *plane) +{ + struct drm_device *dev = plane->dev; + struct phytium_display_private *priv = dev->dev_private; + struct phytium_plane *phytium_plane = to_phytium_plane(plane); + struct drm_framebuffer *fb = plane->state->fb; + int phys_pipe = phytium_plane->phys_pipe; + int config; + unsigned long iova; + + phytium_plane->enable = 1; + phytium_plane->cursor_hot_x = fb->hot_x; + phytium_plane->cursor_hot_y = fb->hot_y; + phytium_plane->cursor_x = plane->state->crtc_x + fb->hot_x; + phytium_plane->cursor_y = plane->state->crtc_y + fb->hot_y; + + if (phytium_plane->cursor_x < 0) { + phytium_plane->cursor_hot_x = plane->state->crtc_w - 1; + phytium_plane->cursor_x = plane->state->crtc_x + phytium_plane->cursor_hot_x; + } + + if (phytium_plane->cursor_y < 0) { + phytium_plane->cursor_hot_y = plane->state->crtc_h - 1; + phytium_plane->cursor_y = plane->state->crtc_y + phytium_plane->cursor_hot_y; + } + + config = CURSOR_FORMAT_ARGB8888 | + ((phytium_plane->cursor_hot_y & CURSOR_HOT_Y_MASK) << CURSOR_HOT_Y_SHIFT) | + ((phytium_plane->cursor_hot_x & CURSOR_HOT_X_MASK) << CURSOR_HOT_X_SHIFT); + phytium_writel_reg(priv, config, priv->dc_reg_base[phys_pipe], PHYTIUM_DC_CURSOR_CONFIG); + + config = ((phytium_plane->cursor_x & CURSOR_X_MASK) << CURSOR_X_SHIFT) | + ((phytium_plane->cursor_y & CURSOR_Y_MASK) << CURSOR_Y_SHIFT); + phytium_writel_reg(priv, config, priv->dc_reg_base[phys_pipe], + PHYTIUM_DC_CURSOR_LOCATION); + iova = phytium_plane->iova[0]; + phytium_writel_reg(priv, iova & 0xffffffff, priv->dc_reg_base[phys_pipe], + PHYTIUM_DC_CURSOR_ADDRESS); + if (phytium_plane->dc_hw_update_cursor_hi_addr) + phytium_plane->dc_hw_update_cursor_hi_addr(plane, iova); +} + +static void phytium_plane_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct drm_framebuffer *fb, *old_fb; + + DRM_DEBUG_KMS("update plane: type=%d\n", plane->type); + if (!plane->state->crtc || !plane->state->fb) + return; + + fb = plane->state->fb; + old_fb = old_state->fb; + + if (fb) + drm_framebuffer_get(fb); + if (old_fb) + drm_framebuffer_put(old_fb); + + phytium_dc_get_plane_parameter(plane); + + if (plane->type == DRM_PLANE_TYPE_PRIMARY) + phytium_dc_primary_plane_update(plane); + else if (plane->type == DRM_PLANE_TYPE_CURSOR) + phytium_dc_cursor_plane_update(plane); +} + +static void phytium_plane_atomic_disable(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct drm_device *dev = plane->dev; + struct phytium_display_private *priv = dev->dev_private; + struct phytium_plane *phytium_plane = to_phytium_plane(plane); + int phys_pipe = phytium_plane->phys_pipe; + int config; + struct drm_framebuffer *old_fb; + + old_fb = old_state->fb; + if (old_fb) + drm_framebuffer_put(old_fb); + + if (plane->type == DRM_PLANE_TYPE_PRIMARY) { + phytium_writel_reg(priv, CLEAR_VALUE_RED, priv->dc_reg_base[phys_pipe], + PHYTIUM_DC_FRAMEBUFFER_CLEARVALUE); + config = phytium_readl_reg(priv, priv->dc_reg_base[phys_pipe], + PHYTIUM_DC_FRAMEBUFFER_CONFIG); + config |= FRAMEBUFFER_CLEAR; + phytium_writel_reg(priv, config, priv->dc_reg_base[phys_pipe], + PHYTIUM_DC_FRAMEBUFFER_CONFIG); + } else if (plane->type == DRM_PLANE_TYPE_CURSOR) { + phytium_writel_reg(priv, CURSOR_FORMAT_DISABLED, + priv->dc_reg_base[phys_pipe], PHYTIUM_DC_CURSOR_CONFIG); + } +} + +const struct drm_plane_helper_funcs phytium_plane_helper_funcs = { + .prepare_fb = phytium_plane_prepare_fb, + .atomic_check = phytium_plane_atomic_check, + .atomic_update = phytium_plane_atomic_update, + .atomic_disable = phytium_plane_atomic_disable, +}; + +struct phytium_plane *phytium_primary_plane_create(struct drm_device *dev, int phys_pipe) +{ + struct phytium_display_private *priv = dev->dev_private; + struct phytium_plane *phytium_plane = NULL; + struct phytium_plane_state *phytium_plane_state = NULL; + int ret = 0; + unsigned int flags = 0; + const uint32_t *formats = NULL; + uint32_t format_count; + const uint64_t *format_modifiers; + + phytium_plane = kzalloc(sizeof(*phytium_plane), GFP_KERNEL); + if (!phytium_plane) { + ret = -ENOMEM; + goto failed_malloc_plane; + } + + phytium_plane_state = kzalloc(sizeof(*phytium_plane_state), GFP_KERNEL); + if (!phytium_plane_state) { + ret = -ENOMEM; + goto failed_malloc_plane_state; + } + phytium_plane_state->base.plane = &phytium_plane->base; + phytium_plane_state->base.rotation = DRM_MODE_ROTATE_0; + phytium_plane->base.state = &phytium_plane_state->base; + phytium_plane->phys_pipe = phys_pipe; + + if (IS_PX210(priv)) { + phytium_plane->dc_hw_plane_get_format = px210_dc_hw_plane_get_primary_format; + phytium_plane->dc_hw_update_dcreq = px210_dc_hw_update_dcreq; + phytium_plane->dc_hw_update_primary_hi_addr = px210_dc_hw_update_primary_hi_addr; + phytium_plane->dc_hw_update_cursor_hi_addr = NULL; + } else if (IS_PE220X(priv)) { + phytium_plane->dc_hw_plane_get_format = pe220x_dc_hw_plane_get_primary_format; + phytium_plane->dc_hw_update_dcreq = NULL; + phytium_plane->dc_hw_update_primary_hi_addr = pe220x_dc_hw_update_primary_hi_addr; + phytium_plane->dc_hw_update_cursor_hi_addr = NULL; + } + + phytium_plane->dc_hw_plane_get_format(&format_modifiers, &formats, &format_count); + ret = drm_universal_plane_init(dev, &phytium_plane->base, 0x0, + &phytium_plane_funcs, formats, + format_count, + format_modifiers, + DRM_PLANE_TYPE_PRIMARY, "primary %d", phys_pipe); + + if (ret) + goto failed_plane_init; + + flags = DRM_MODE_ROTATE_0; + drm_plane_create_rotation_property(&phytium_plane->base, DRM_MODE_ROTATE_0, flags); + drm_plane_helper_add(&phytium_plane->base, &phytium_plane_helper_funcs); + + return phytium_plane; +failed_plane_init: + kfree(phytium_plane_state); +failed_malloc_plane_state: + kfree(phytium_plane); +failed_malloc_plane: + return ERR_PTR(ret); +} + +struct phytium_plane *phytium_cursor_plane_create(struct drm_device *dev, int phys_pipe) +{ + struct phytium_display_private *priv = dev->dev_private; + struct phytium_plane *phytium_plane = NULL; + struct phytium_plane_state *phytium_plane_state = NULL; + int ret = 0; + unsigned int flags = 0; + const uint32_t *formats = NULL; + uint32_t format_count; + const uint64_t *format_modifiers; + + phytium_plane = kzalloc(sizeof(*phytium_plane), GFP_KERNEL); + if (!phytium_plane) { + ret = -ENOMEM; + goto failed_malloc_plane; + } + + phytium_plane_state = kzalloc(sizeof(*phytium_plane_state), GFP_KERNEL); + if (!phytium_plane_state) { + ret = -ENOMEM; + goto failed_malloc_plane_state; + } + phytium_plane_state->base.plane = &phytium_plane->base; + phytium_plane_state->base.rotation = DRM_MODE_ROTATE_0; + phytium_plane->base.state = &phytium_plane_state->base; + phytium_plane->phys_pipe = phys_pipe; + + if (IS_PX210(priv)) { + phytium_plane->dc_hw_plane_get_format = px210_dc_hw_plane_get_cursor_format; + phytium_plane->dc_hw_update_dcreq = NULL; + phytium_plane->dc_hw_update_primary_hi_addr = NULL; + phytium_plane->dc_hw_update_cursor_hi_addr = NULL; + } else if (IS_PE220X(priv)) { + phytium_plane->dc_hw_plane_get_format = pe220x_dc_hw_plane_get_cursor_format; + phytium_plane->dc_hw_update_dcreq = NULL; + phytium_plane->dc_hw_update_primary_hi_addr = NULL; + phytium_plane->dc_hw_update_cursor_hi_addr = pe220x_dc_hw_update_cursor_hi_addr; + } + + phytium_plane->dc_hw_plane_get_format(&format_modifiers, &formats, &format_count); + ret = drm_universal_plane_init(dev, &phytium_plane->base, 0x0, + &phytium_plane_funcs, + formats, format_count, + format_modifiers, + DRM_PLANE_TYPE_CURSOR, "cursor %d", phys_pipe); + + if (ret) + goto failed_plane_init; + + flags = DRM_MODE_ROTATE_0; + drm_plane_create_rotation_property(&phytium_plane->base, DRM_MODE_ROTATE_0, flags); + drm_plane_helper_add(&phytium_plane->base, &phytium_plane_helper_funcs); + + return phytium_plane; +failed_plane_init: + kfree(phytium_plane_state); +failed_malloc_plane_state: + kfree(phytium_plane); +failed_malloc_plane: + return ERR_PTR(ret); +} diff --git a/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_plane.h b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_plane.h new file mode 100644 index 000000000..ee8786ced --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_plane.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Phytium display drm driver + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#ifndef __PHYTIUM_PLANE_H__ +#define __PHYTIUM_PLANE_H__ + +struct phytium_plane { + struct drm_plane base; + int phys_pipe; + unsigned long iova[PHYTIUM_FORMAT_MAX_PLANE]; + unsigned long size[PHYTIUM_FORMAT_MAX_PLANE]; + unsigned int format; + unsigned int tiling[PHYTIUM_FORMAT_MAX_PLANE]; + unsigned int swizzle; + unsigned int uv_swizzle; + unsigned int rot_angle; + + /* only for cursor */ + bool enable; + bool reserve[3]; + int cursor_x; + int cursor_y; + int cursor_hot_x; + int cursor_hot_y; + + void (*dc_hw_plane_get_format)(const uint64_t **format_modifiers, + const uint32_t **formats, + uint32_t *format_count); + void (*dc_hw_update_dcreq)(struct drm_plane *plane); + void (*dc_hw_update_primary_hi_addr)(struct drm_plane *plane); + void (*dc_hw_update_cursor_hi_addr)(struct drm_plane *plane, uint64_t iova); +}; + +struct phytium_plane_state { + struct drm_plane_state base; +}; + +#define to_phytium_plane(x) container_of(x, struct phytium_plane, base) +#define to_phytium_plane_state(x) container_of(x, struct phytium_plane_state, base) + +struct phytium_plane *phytium_primary_plane_create(struct drm_device *dev, int pipe); +struct phytium_plane *phytium_cursor_plane_create(struct drm_device *dev, int pipe); +#endif /* __PHYTIUM_PLANE_H__ */ diff --git a/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_platform.c b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_platform.c new file mode 100644 index 000000000..27815ecc9 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_platform.c @@ -0,0 +1,309 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Phytium display engine DRM driver + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include "phytium_display_drv.h" +#include "phytium_platform.h" +#include "phytium_dp.h" +#include "phytium_gem.h" +#include "pe220x_dc.h" +#include "pe220x_dp.h" + +int phytium_platform_carveout_mem_init(struct platform_device *pdev, + struct phytium_display_private *priv) +{ + struct resource *res; + int ret = 0; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (res) { + priv->pool_size = resource_size(res); + priv->pool_phys_addr = res->start; + } + + if ((priv->pool_phys_addr != 0) && (priv->pool_size != 0)) { + priv->pool_virt_addr = ioremap_cache(priv->pool_phys_addr, priv->pool_size); + if (priv->pool_virt_addr == NULL) { + DRM_ERROR("failed to remap carveout mem(0x%llx)\n", priv->pool_phys_addr); + ret = -EINVAL; + goto failed_ioremap; + } + ret = phytium_memory_pool_init(&pdev->dev, priv); + if (ret) + goto failed_init_memory_pool; + + priv->mem_state[PHYTIUM_MEM_SYSTEM_CARVEOUT_TOTAL] = priv->pool_size; + priv->support_memory_type = MEMORY_TYPE_SYSTEM_CARVEOUT; + priv->vram_hw_init = NULL; + } else { + DRM_DEBUG_KMS("not support carveout memory\n"); + priv->mem_state[PHYTIUM_MEM_SYSTEM_CARVEOUT_TOTAL] = 0; + priv->support_memory_type = MEMORY_TYPE_SYSTEM_UNIFIED; + priv->vram_hw_init = NULL; + } + + return 0; + +failed_init_memory_pool: + iounmap(priv->pool_virt_addr); +failed_ioremap: + return ret; +} + +void phytium_platform_carveout_mem_fini(struct platform_device *pdev, + struct phytium_display_private *priv) +{ + if (priv->support_memory_type == MEMORY_TYPE_SYSTEM_CARVEOUT) { + phytium_memory_pool_fini(&pdev->dev, priv); + iounmap(priv->pool_virt_addr); + } +} + +static struct phytium_display_private * +phytium_platform_private_init(struct platform_device *pdev) +{ + struct drm_device *dev = dev_get_drvdata(&pdev->dev); + struct device_node *node; + struct fwnode_handle *np; + struct phytium_display_private *priv = NULL; + struct phytium_platform_private *platform_priv = NULL; + struct phytium_device_info *phytium_info = NULL; + int i = 0, ret = 0; + struct resource *res; + + platform_priv = devm_kzalloc(&pdev->dev, sizeof(*platform_priv), GFP_KERNEL); + if (!platform_priv) { + DRM_ERROR("no memory to allocate for phytium_platform_private\n"); + goto exit; + } + + memset(platform_priv, 0, sizeof(*platform_priv)); + priv = &platform_priv->base; + phytium_display_private_init(priv, dev); + + if (pdev->dev.of_node) { + phytium_info = (struct phytium_device_info *)of_device_get_match_data(&pdev->dev); + if (!phytium_info) { + DRM_ERROR("failed to get dts id data(phytium_info)\n"); + goto failed; + } + + memcpy(&(priv->info), phytium_info, sizeof(struct phytium_device_info)); + node = pdev->dev.of_node; + ret = of_property_read_u8(node, "pipe_mask", &priv->info.pipe_mask); + if (ret < 0) { + dev_err(&pdev->dev, "missing pipe_mask property from dts\n"); + goto failed; + } + + ret = of_property_read_u8(node, "edp_mask", &priv->info.edp_mask); + if (ret < 0) { + dev_err(&pdev->dev, "missing edp_mask property from dts\n"); + goto failed; + } + } else if (has_acpi_companion(&pdev->dev)) { + phytium_info = (struct phytium_device_info *)acpi_device_get_match_data(&pdev->dev); + if (!phytium_info) { + DRM_ERROR("failed to get acpi id data(phytium_info)\n"); + goto failed; + } + + memcpy(&(priv->info), phytium_info, sizeof(struct phytium_device_info)); + np = dev_fwnode(&(pdev->dev)); + ret = fwnode_property_read_u8(np, "pipe_mask", &priv->info.pipe_mask); + if (ret < 0) { + dev_err(&pdev->dev, "missing pipe_mask property from acpi\n"); + goto failed; + } + ret = fwnode_property_read_u8(np, "edp_mask", &priv->info.edp_mask); + if (ret < 0) { + dev_err(&pdev->dev, "missing edp_mask property from acpi\n"); + goto failed; + } + } + + priv->info.num_pipes = 0; + for_each_pipe_masked(priv, i) + priv->info.num_pipes++; + if (priv->info.num_pipes == 0) { + DRM_ERROR("num_pipes is zero, so exit init\n"); + goto failed; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->regs = devm_ioremap_resource(&pdev->dev, res); + if (priv->regs == NULL) { + DRM_ERROR("ioremap fail, addr:0x%llx, size:0x%llx\n", res->start, res->end); + goto failed; + } + + priv->irq = platform_get_irq(pdev, 0); + if (priv->irq < 0) { + dev_err(&pdev->dev, "failed to get irq\n"); + goto failed; + } + + if (IS_PE220X(priv)) { + priv->dc_hw_clear_msi_irq = NULL; + priv->dc_hw_fb_format_check = pe220x_dc_hw_fb_format_check; + } + + return priv; + +failed: + devm_kfree(&pdev->dev, platform_priv); +exit: + return NULL; +} + +static void phytium_platform_private_fini(struct platform_device *pdev) +{ + struct drm_device *dev = dev_get_drvdata(&pdev->dev); + struct phytium_display_private *priv = dev->dev_private; + struct phytium_platform_private *platform_priv = to_platform_priv(priv); + + devm_kfree(&pdev->dev, platform_priv); +} + +static int phytium_platform_probe(struct platform_device *pdev) +{ + struct phytium_display_private *priv = NULL; + struct drm_device *dev = NULL; + int ret = 0; + + dev = drm_dev_alloc(&phytium_display_drm_driver, &pdev->dev); + if (IS_ERR(dev)) { + DRM_ERROR("failed to allocate drm_device\n"); + return PTR_ERR(dev); + } + + dev_set_drvdata(&pdev->dev, dev); + dma_set_mask(&pdev->dev, DMA_BIT_MASK(40)); + + priv = phytium_platform_private_init(pdev); + if (priv) + dev->dev_private = priv; + else + goto failed_platform_private_init; + + ret = phytium_platform_carveout_mem_init(pdev, priv); + if (ret) { + DRM_ERROR("failed to init system carveout memory\n"); + goto failed_carveout_mem_init; + } + + ret = drm_dev_register(dev, 0); + if (ret) { + DRM_ERROR("failed to register drm dev\n"); + goto failed_register_drm; + } + + phytium_dp_hpd_irq_setup(dev, true); + + return 0; + +failed_register_drm: + phytium_platform_carveout_mem_fini(pdev, priv); +failed_carveout_mem_init: + phytium_platform_private_fini(pdev); +failed_platform_private_init: + dev_set_drvdata(&pdev->dev, NULL); + drm_dev_put(dev); + return -1; +} + +static int phytium_platform_remove(struct platform_device *pdev) +{ + struct drm_device *dev = dev_get_drvdata(&pdev->dev); + struct phytium_display_private *priv = dev->dev_private; + + phytium_dp_hpd_irq_setup(dev, false); + cancel_work_sync(&priv->hotplug_work); + drm_dev_unregister(dev); + phytium_platform_private_fini(pdev); + dev_set_drvdata(&pdev->dev, NULL); + drm_dev_put(dev); + + return 0; +} + +static void phytium_platform_shutdown(struct platform_device *pdev) +{ + struct drm_device *dev = dev_get_drvdata(&pdev->dev); + struct phytium_display_private *priv = dev->dev_private; + + priv->display_shutdown(dev); +} + +static int phytium_platform_pm_suspend(struct device *dev) +{ + struct drm_device *drm_dev = dev_get_drvdata(dev); + struct phytium_display_private *priv = drm_dev->dev_private; + + return priv->display_pm_suspend(drm_dev); +} + +static int phytium_platform_pm_resume(struct device *dev) +{ + struct drm_device *drm_dev = dev_get_drvdata(dev); + struct phytium_display_private *priv = drm_dev->dev_private; + + return priv->display_pm_resume(drm_dev); +} + +static const struct dev_pm_ops phytium_platform_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(phytium_platform_pm_suspend, phytium_platform_pm_resume) +}; + +static const struct phytium_device_info pe220x_info = { + .platform_mask = BIT(PHYTIUM_PLATFORM_PE220X), + .total_pipes = 2, + .crtc_clock_max = PE220X_DC_PIX_CLOCK_MAX, + .hdisplay_max = PE220X_DC_HDISPLAY_MAX, + .vdisplay_max = PE220X_DC_VDISPLAY_MAX, + .address_mask = PE220X_DC_ADDRESS_MASK, + .backlight_max = PE220X_DP_BACKLIGHT_MAX, +}; + +static const struct of_device_id display_of_match[] = { + { + .compatible = "phytium,dc", + .data = (void*)&pe220x_info, + }, + { } +}; + +MODULE_DEVICE_TABLE(of, display_of_match); + +#ifdef CONFIG_ACPI +static const struct acpi_device_id display_acpi_ids[] = { + { + .id = "PHYT0015", + .driver_data = (kernel_ulong_t)&pe220x_info, + }, + {}, +}; + +MODULE_DEVICE_TABLE(acpi, display_acpi_ids); +#else +#define display_acpi_ids NULL +#endif + +struct platform_driver phytium_platform_driver = { + .driver = { + .name = "phytium_display_platform", + .of_match_table = of_match_ptr(display_of_match), + .acpi_match_table = ACPI_PTR(display_acpi_ids), + }, + .probe = phytium_platform_probe, + .remove = phytium_platform_remove, + .shutdown = phytium_platform_shutdown, + .driver.pm = &phytium_platform_pm_ops, +}; diff --git a/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_platform.h b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_platform.h new file mode 100644 index 000000000..e752f7913 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_platform.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Phytium display drm driver + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#ifndef __PHYTIUM_PLATFORM_H__ +#define __PHYTIUM_PLATFORM_H__ + +struct phytium_platform_private { + struct phytium_display_private base; +}; + +#define to_platform_priv(priv) container_of(priv, struct phytium_platform_private, base) + +extern struct platform_driver phytium_platform_driver; + +#endif /* __PHYTIUM_PLATFORM_H__ */ diff --git a/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_reg.h b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_reg.h new file mode 100644 index 000000000..28735acee --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/phytium_reg.h @@ -0,0 +1,366 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Phytium display drm driver + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#ifndef __PHYTIUM_REG_H__ +#define __PHYTIUM_REG_H__ + +/******************************register base******************************************/ +#define PX210_PIPE_BASE(pipe) (0x8000*pipe) +#define PX210_DC_BASE(pipe) (PX210_PIPE_BASE(pipe) + 0x0000) +#define PX210_DCREQ_BASE(pipe) (PX210_PIPE_BASE(pipe) + 0x2000) +#define PX210_DP_BASE(pipe) (PX210_PIPE_BASE(pipe) + 0x3000) +#define PX210_ADDRESS_TRANSFORM_BASE 0x4000 +#define PX210_PHY_ACCESS_BASE(pipe) (PX210_PIPE_BASE(pipe) + 0x5000) + +#define PE220X_DC_BASE(pipe) (0x1000*pipe) +#define PE220X_DP_BASE(pipe) (0x4000 + 0x1000*pipe) +#define PE220X_ADDRESS_TRANSFORM_BASE 0x8000 +#define PE220X_PHY_ACCESS_BASE(pipe) (0x6000 + 0x1000*pipe) +/******************************register base end******************************************/ + +/******************************dc register start******************************************/ +#define PHYTIUM_DC_FRAMEBUFFER_Y_ADDRESS 0x1400 + #define ADDRESS_MASK 0xffffff80 +#define PHYTIUM_DC_FRAMEBUFFER_Y_STRIDE 0x1408 +#define PHYTIUM_DC_PANEL_CONFIG 0x1418 + #define PANEL_DATAENABLE_ENABLE (1<<0) + #define PANEL_DATA_ENABLE (1<<4) + #define PANEL_CLOCK_ENABLE (1<<8) +#define PHYTIUM_DC_HDISPLAY 0x1430 + #define HDISPLAY_END_SHIFT 0 + #define HDISPLAY_END_MASK 0x7fff + #define HDISPLAY_TOTAL_SHIFT 16 + #define HDISPLAY_TOTAL_MASK 0x7fff +#define PHYTIUM_DC_HSYNC 0x1438 + #define HSYNC_START_SHIFT 0 + #define HSYNC_START_MASK 0x7fff + #define HSYNC_END_SHIFT 15 + #define HSYNC_END_MASK 0x7fff + #define HSYNC_PULSE_ENABLED (1<<30) + #define HSYNC_NEGATIVE (1<<31) +#define PHYTIUM_DC_VDISPLAY 0x1440 + #define VDISPLAY_END_SHIFT 0 + #define VDISPLAY_END_MASK 0x7fff + #define VDISPLAY_TOTAL_SHIFT 16 + #define VDISPLAY_TOTAL_MASK 0x7fff +#define PHYTIUM_DC_VSYNC 0x1448 + #define VSYNC_START_SHIFT 0 + #define VSYNC_START_MASK 0x7fff + #define VSYNC_END_SHIFT 15 + #define VSYNC_END_MASK 0x7fff + #define VSYNC_PULSE_ENABLED (1<<30) + #define VSYNC_NEGATIVE (1<<31) +#define PHYTIUM_DC_LOCATION 0x1450 + #define LOVATION_Y_SHIFT 16 +#define PHYTIUM_DC_GAMMA_INDEX 0x1458 + #define GAMMA_INDEX_MAX 256 +#define PHYTIUM_DC_GAMMA_DATA 0x1460 + #define GAMMA_BLUE_SHIFT 0 + #define GAMMA_BLUE_MASK 0x3ff + #define GAMMA_GREEN_SHIFT 10 + #define GAMMA_GREEN_MASK 0x3ff + #define GAMMA_RED_SHIFT 20 + #define GAMMA_RED_MASK 0x3ff +#define PHYTIUM_DC_CURSOR_CONFIG 0x1468 + #define CURSOR_FORMAT_DISABLED 0x0 + #define CURSOR_FORMAT_MASKMODE 0x3 + #define CURSOR_FORMAT_ARGB8888 0x2 + #define CURSOR_FORMAT_MASK 0x3 + #define CURSOR_HOT_Y_SHIFT 8 + #define CURSOR_HOT_Y_MASK 0x1f + #define CURSOR_HOT_X_SHIFT 16 + #define CURSOR_HOT_X_MASK 0x1f +#define PHYTIUM_DC_CURSOR_ADDRESS 0x146c +#define PHYTIUM_DC_CURSOR_LOCATION 0x1470 + #define CURSOR_X_SHIFT 0 + #define CURSOR_X_MASK 0x7fff + #define CURSOR_Y_SHIFT 16 + #define CURSOR_Y_MASK 0x7fff +#define PHYTIUM_DC_CURSOR_BACKGROUND 0x1474 +#define PHYTIUM_DC_CURSOR_FOREGROUND 0x1478 +#define PHYTIUM_DC_INT_STATUS 0x147c + #define INT_STATUS 0x1 +#define PHYTIUM_DC_INT_ENABLE 0x1480 + #define INT_ENABLE 0x1 + #define INT_DISABLE 0x0 + +#define PHYTIUM_DC_FRAMEBUFFER_CONFIG 0x1518 + #define FRAMEBUFFER_OUTPUT BIT(0) + #define FRAMEBUFFER_GAMMA_ENABLE BIT(2) + #define FRAMEBUFFER_VALID_PENDING BIT(3) + #define FRAMEBUFFER_RESET BIT(4) + #define FRAMEBUFFER_PROGRESS BIT(6) + #define FRAMEBUFFER_ROT_ANGLE_SHIFT (11) + #define FRAMEBUFFER_ROT_ANGLE_MASK (0x7) + #define FRAMEBUFFER_ROT_ANGLE_ROT0 (0) + #define FRAMEBUFFER_ROT_ANGLE_FLIP_X (1) + #define FRAMEBUFFER_ROT_ANGLE_FLIP_Y (2) + #define FRAMEBUFFER_TILE_MODE_SHIFT (17) + #define FRAMEBUFFER_TILE_MODE_MASK (0x1f) + #define FRAMEBUFFER_LINEAR 0 + #define FRAMEBUFFER_TILE_MODE0 4 + #define FRAMEBUFFER_TILE_MODE3 7 + #define FRAMEBUFFER_FORMAT_SHIFT 26 + #define FRAMEBUFFER_FORMAT_MASK 0x3f + #define FRAMEBUFFER_FORMAT_XRGB4444 0x0 + #define FRAMEBUFFER_FORMAT_ARGB4444 0x1 + #define FRAMEBUFFER_FORMAT_XRGB1555 0x2 + #define FRAMEBUFFER_FORMAT_ARGB1555 0x3 + #define FRAMEBUFFER_FORMAT_RGB565 0x4 + #define FRAMEBUFFER_FORMAT_XRGB8888 0x5 + #define FRAMEBUFFER_FORMAT_ARGB8888 0x6 + #define FRAMEBUFFER_FORMAT_YUYV 0x7 + #define FRAMEBUFFER_FORMAT_UYVY 0x8 + #define FRAMEBUFFER_FORMAT_NV12 0x11 + #define FRAMEBUFFER_FORMAT_NV16 0x12 + #define FRAMEBUFFER_FORMAT_ARGB2101010 0x16 + #define FRAMEBUFFER_SWIZZLE_SHIFT 23 + #define FRAMEBUFFER_SWIZZLE_MASK 0x3 + #define FRAMEBUFFER_SWIZZLE_ARGB 0 + #define FRAMEBUFFER_SWIZZLE_RGBA 1 + #define FRAMEBUFFER_SWIZZLE_ABGR 2 + #define FRAMEBUFFER_SWIZZLE_BGRA 3 + #define FRAMEBUFFER_UVSWIZZLE_SHIFT 25 + #define FRAMEBUFFER_UVSWIZZLE_DISABLE 0 + #define FRAMEBUFFER_UVSWIZZLE_ENABLE 1 + #define FRAMEBUFFER_CLEAR BIT(8) + #define FRAMEBUFFER_SCALE_ENABLE BIT(22) +#define PHYTIUM_DC_FRAMEBUFFER_SCALECONFIG 0x1520 + #define FRAMEBUFFER_FILTER_TAP 3 + #define FRAMEBUFFER_HORIZONTAL_FILTER_TAP 3 + #define FRAMEBUFFER_TAP 0x33 +#define PHYTIUM_DC_FRAMEBUFFER_U_ADDRESS 0x1530 +#define PHYTIUM_DC_FRAMEBUFFER_V_ADDRESS 0x1538 +#define PHYTIUM_DC_OVERLAY_CONFIG 0x1540 + #define PX210_DC_OVERLAY_ENABLE BIT(24) + +#define PHYTIUM_DC_FRAMEBUFFER_U_STRIDE 0x1800 +#define PHYTIUM_DC_FRAMEBUFFER_V_STRIDE 0x1808 +#define PHYTIUM_DC_FRAMEBUFFER_SIZE 0x1810 + #define WIDTH_SHIFT 0 + #define WIDTH_MASK 0x7fff + #define HEIGHT_SHIFT 15 + #define HEIGHT_MASK 0x7fff + +#define PHYTIUM_DC_FRAMEBUFFER_SCALE_FACTOR_X 0x1828 + #define SCALE_FACTOR_X_MASK 0x7fffffff +#define PHYTIUM_DC_FRAMEBUFFER_SCALE_FACTOR_Y 0x1830 + #define SCALE_FACTOR_Y_MASK 0x7fffffff + #define SCALE_FACTOR_Y_MAX 0x3 + #define SCALE_FACTOR_SRC_OFFSET 16 + +#define PHYTIUM_DC_FRAMEBUFFER_HORI_FILTER_INDEX 0x1838 + #define HORI_FILTER_INDEX 0x0 +#define PHYTIUM_DC_FRAMEBUFFER_HORI_FILTER 0x1a00 +#define PHYTIUM_DC_FRAMEBUFFER_VERT_FILTER_INDEX 0x1a08 + #define VERT_FILTER_INDEX 0x0 +#define PHYTIUM_DC_FRAMEBUFFER_VERT_FILTER 0x1a10 +#define PHYTIUM_DC_FRAMEBUFFER_CLEARVALUE 0x1a18 + #define CLEAR_VALUE_RED 0x00ff0000 + #define CLEAR_VALUE_GREEN 0x0000ff00 + #define CLEAR_VALUE_BLACK 0x00000000 +#define PHYTIUM_DC_FRAMEBUFFER_INITIALOFFSET 0x1a20 + #define INITIALOFFSET (0x8000 | (0X8000 << 16)) +#define PHYTIUM_DC_DP_CONFIG 0x1cd0 + #define OUTPUT_DP (1<<3) + #define DP_RGB666 (0x1) + #define DP_RGB888 (0x2) + #define DP_RGB101010 (0x3) +/******************************dc register end********************************************/ + +/******************************phy access register****************************************/ +#define PHYTIUM_PHY_ACCESS_ADDRESS 0x0000 +#define PHYTIUM_PHY_WRITE_DATA 0x0004 +#define PHYTIUM_PHY_READ_DATA 0x0008 +#define PHYTIUM_PHY_ACCESS_CTRL 0x000c + #define ACCESS_WRITE (1<<0) + #define ACCESS_READ (1<<1) +/******************************phy access register end*************************************/ + +/******************************dp register start******************************************/ +#define PHYTIUM_DP_LINK_BW_SET 0x0000 +#define PHYTIUM_DP_LANE_COUNT_SET 0x0004 +#define PHYTIUM_DP_ENHANCED_FRAME_EN 0x0008 + #define ENHANCED_FRAME_ENABLE 0x1 + #define ENHANCED_FRAME_DISABLE 0x0 +#define PHYTIUM_DP_TRAINING_PATTERN_SET 0x000c + #define TRAINING_OFF 0x0 + #define TRAINING_PATTERN_1 0x1 + #define TRAINING_PATTERN_2 0x2 + #define TRAINING_PATTERN_3 0x3 + #define TRAINING_PATTERN_4 0x4 +#define PHYTIUM_DP_LINK_QUAL_PATTERN_SET 0x0010 + #define TEST_PATTERN_NONE 0x0 + #define TEST_PATTERN_D10_2 0x1 + #define TEST_PATTERN_SYMBOL_ERROR 0x2 + #define TEST_PATTERN_PRBS7 0x3 + #define TEST_PATTERN_80BIT_CUSTOM 0x4 + #define TEST_PATTERN_CP2520_1 0x5 + #define TEST_PATTERN_CP2520_2 0x6 + #define TEST_PATTERN_CP2520_3 0x7 + #define TEST_PATTERN_LANE_SHIFT 8 +#define PHYTIUM_DP_SCRAMBLING_DISABLE 0x0014 + #define SCRAMBLING_ENABLE 0x0 + #define SCRAMBLING_DISABLE 0x1 +#define PHYTIUM_DP_DOWNSPREAD_CTRL 0x0018 +#define PHYTIUM_DP_ALT_SCRAMBLER_RESET 0x001c +#define PHYTIUM_DP_HBR2_SCRAMBLER_RESET 0x0020 +#define PHYTIUM_DP_DISPLAYPORT_VERSION 0x0024 +#define PHYTIUM_DP_CUSTOM_80BIT_PATTERN_0 0x0030 +#define PHYTIUM_DP_CUSTOM_80BIT_PATTERN_1 0x0034 +#define PHYTIUM_DP_CUSTOM_80BIT_PATTERN_2 0x0038 +#define PHYTIUM_DP_TRANSMITTER_OUTPUT_ENABLE 0x0080 + #define TRANSMITTER_OUTPUT_ENABLE BIT(0) + #define TRANSMITTER_OUTPUT_DISABLE 0 +#define PHYTIUM_DP_VIDEO_STREAM_ENABLE 0x0084 + #define SST_MST_SOURCE_0_ENABLE BIT(0) + #define SST_MST_SOURCE_0_ENABLE_MASK 0x1 + #define SST_MST_SOURCE_0_DISABLE 0 +#define PHYTIUM_DP_SECONDARY_STREAM_ENABLE 0x0088 + #define SECONDARY_STREAM_ENABLE 0x1 + #define SECONDARY_STREAM_DISABLE 0x0 +#define PHYTIUM_DP_SEC_DATA_WINDOW 0x008C +#define PHYTIUM_DP_SOFT_RESET 0x0090 + #define LINK_SOFT_RESET (0x1 << 0) + #define VIDEO_SOFT_RESET (0x1 << 1) +#define PHYTIUM_INPUT_SOURCE_ENABLE 0x0094 + #define VIRTUAL_SOURCE_0_ENABLE BIT(0) + #define VIRTUAL_SOURCE_0_ENABLE_MASK 0x1 +#define PHYTIUM_DP_FORCE_SCRAMBLER_RESET 0x00C0 + #define SCRAMBLER_RESET BIT(0) +#define PHYTIUM_DP_SOURCE_CONTROL_STATUS 0x00C4 +#define PHYTIUM_DP_DATA_CONTROL 0x00C8 +#define PHYTIUM_DP_CORE_CAPABILITY 0x00F8 +#define PHYTIUM_DP_CORE_ID 0x00FC +#define PHYTIUM_DP_AUX_COMMAND 0x0100 + #define BYTE_COUNT_MASK 0xf + #define COMMAND_SHIFT 8 + #define COMMAND_MASK 0xf + #define ADDRESS_ONLY (1<<12) +#define PHYTIUM_DP_AUX_WRITE_FIFO 0x0104 +#define PHYTIUM_DP_AUX_ADDRESS 0x0108 +#define PHYTIUM_DP_AUX_CLK_DIVIDER 0x010C + #define AUX_CLK_DIVIDER 48 + #define AUX_CLK_DIVIDER_100 100 +#define PHYTIUM_DP_SINK_HPD_STATE 0x0128 + #define HPD_CONNECT 0x1 + #define HPD_DISCONNECT 0x0 +#define PHYTIUM_DP_INTERRUPT_RAW_STATUS 0x0130 + #define REPLY_TIMEOUT (1<<3) + #define DP_STATUS_REQUEST_IN_PROGRESS (1<<1) + #define HPD_STATE (0<<1) +#define PHYTIUM_DP_AUX_REPLY_DATA 0x0134 +#define PHYTIUM_DP_AUX_REPLY_CODE 0x0138 + #define AUX_NATIVE_ACK (0x0<<0) + #define AUX_NATIVE_NACK (0x1<<0) + #define AUX_NATIVE_DEFER (0x2<<0) + #define AUX_NATIVE_MASK (0x3 << 0) + #define AUX_I2C_ACK (0x0<<2) + #define AUX_I2C_NACK (0x1<<2) + #define AUX_I2C_DEFER (0x2<<2) + #define AUX_I2C_MASK (0x3 << 2) +#define PHYTIUM_DP_INTERRUPT_STATUS 0x0140 + #define HPD_IRQ (1<<1) + #define HPD_EVENT (1<<0) +#define PHYTIUM_DP_INTERRUPT_MASK 0x0144 + #define HPD_IRQ_MASK (1<<1) + #define HPD_EVENT_MASK (1<<0) + #define HPD_OTHER_MASK 0x3c +#define PHYTIUM_DP_AUX_REPLY_DATA_COUNT 0x0148 +#define PHYTIUM_DP_AUX_STATUS 0x014C + #define REPLY_RECEIVED 0x1 + #define REPLY_IN_PROGRESS 0x2 + #define REQUEST_IN_PROGRESS 0x4 + #define REPLY_ERROR 0x8 +#define PHYTIUM_DP_AUX_TIMER 0x0158 +#define PHYTIUM_DP_MAIN_LINK_HTOTAL 0x0180 +#define PHYTIUM_DP_MAIN_LINK_VTOTAL 0x0184 +#define PHYTIUM_DP_MAIN_LINK_POLARITY 0x0188 + #define VSYNC_POLARITY_LOW BIT(1) + #define HSYNC_POLARITY_LOW BIT(0) +#define PHYTIUM_DP_MAIN_LINK_HSWIDTH 0x018C +#define PHYTIUM_DP_MAIN_LINK_VSWIDTH 0x0190 +#define PHYTIUM_DP_MAIN_LINK_HRES 0x0194 +#define PHYTIUM_DP_MAIN_LINK_VRES 0x0198 +#define PHYTIUM_DP_MAIN_LINK_HSTART 0x019C +#define PHYTIUM_DP_MAIN_LINK_VSTART 0x01A0 +#define PHYTIUM_DP_MAIN_LINK_MISC0 0x01A4 + #define MISC0_SYNCHRONOUS_CLOCK BIT(0) + #define MISC0_BIT_DEPTH_OFFSET 5 + #define MISC0_BIT_DEPTH_6BIT 0x0 + #define MISC0_BIT_DEPTH_8BIT 0x1 + #define MISC0_BIT_DEPTH_10BIT 0x2 + #define MISC0_COMPONENT_FORMAT_SHIFT 1 + #define MISC0_COMPONENT_FORMAT_RGB 0x0 +#define PHYTIUM_DP_MAIN_LINK_MISC1 0x01A8 +#define PHYTIUM_DP_M_VID 0x01AC +#define PHYTIUM_DP_TRANSFER_UNIT_SIZE 0x01B0 +#define PHYTIUM_DP_N_VID 0x01B4 +#define PHYTIUM_DP_USER_PIXEL_WIDTH 0x01B8 +#define PHYTIUM_DP_DATA_COUNT 0x01BC +#define PHYTIUM_DP_INTERLACED 0x01C0 +#define PHYTIUM_DP_USER_SYNC_POLARITY 0x01C4 + #define USER_ODDEVEN_POLARITY_HIGH BIT(3) + #define USER_DATA_ENABLE_POLARITY_HIGH BIT(2) + #define USER_VSYNC_POLARITY_HIGH BIT(1) + #define USER_HSYNC_POLARITY_HIGH BIT(0) +#define PHYTIUM_DP_USER_CONTROL 0x01C8 +#define PHYTIUM_EDP_CRC_ENABLE 0x01D0 + #define SUPPORT_EDP_1_4 BIT(1) +#define PHYTIUM_EDP_CRC_RED 0x01D4 +#define PHYTIUM_EDP_CRC_GREEN 0x01D8 +#define PHYTIUM_EDP_CRC_BLUE 0x01DC +#define PHYTIUM_DP_SEC_AUDIO_ENABLE 0x0300 + #define SEC_AUDIO_ENABLE BIT(0) + #define CHANNEL_MUTE_ENABLE BIT(1) +#define PHYTIUM_DP_SEC_INPUT_SELECT 0x0304 + #define INPUT_SELECT_I2S 0x0 +#define PHYTIUM_DP_SEC_CHANNEL_COUNT 0x0308 + #define CHANNEL_2 0x2 + #define CHANNEL_2_LFE 0x3 + #define CHANNEL_5_1 0x6 + #define CHANNEL_7_1 0x7 + #define CHANNEL_MASK 0xf +#define PHYTIUM_DP_SEC_DIRECT_CLKDIV 0x030c + #define APB_CLOCK 48000000 +#define PHYTIUM_DP_SEC_MAUD 0x0318 +#define PHYTIUM_DP_SEC_NAUD 0x031c +#define PHYTIUM_DP_SEC_CLOCK_MODE 0x0320 + #define CLOCK_MODE_SYNC 0x1 +#define PHYTIUM_DP_SEC_CS_SOURCE_FORMAT 0x0340 + #define CS_SOURCE_FORMAT_DEFAULT 0x0 +#define PHYTIUM_DP_SEC_CS_CATEGORY_CODE 0x0344 +#define PHYTIUM_DP_SEC_CS_LENGTH_ORIG_FREQ 0x0348 + #define ORIG_FREQ_32000 0xc + #define ORIG_FREQ_44100 0xf + #define ORIG_FREQ_48000 0xd + #define ORIG_FREQ_88200 0x7 + #define ORIG_FREQ_96000 0x5 + #define ORIG_FREQ_176400 0x3 + #define ORIG_FREQ_192000 0x1 + #define ORIG_FREQ_MASK 0xf + #define ORIG_FREQ_SHIFT 0 + #define WORD_LENGTH_16 0x4 + #define WORD_LENGTH_18 0x2 + #define WORD_LENGTH_20 0xc + #define WORD_LENGTH_24 0xd + #define WORD_LENGTH_MASK 0xf + #define WORD_LENGTH_SHIFT 4 +#define PHYTIUM_DP_SEC_CS_FREQ_CLOCK_ACCURACY 0x034c // not used + #define SAMPLING_FREQ_32000 0xc + #define SAMPLING_FREQ_44100 0x0 + #define SAMPLING_FREQ_48000 0x4 + #define SAMPLING_FREQ_88200 0x1 + #define SAMPLING_FREQ_96000 0x5 + #define SAMPLING_FREQ_176400 0x3 + #define SAMPLING_FREQ_192000 0x7 + #define SAMPLING_FREQ_MASK 0xf + #define SAMPLING_FREQ_SHIFT 4 +#define PHYTIUM_DP_SEC_CHANNEL_MAP 0x035C + #define CHANNEL_MAP_DEFAULT 0x87654321 +/******************************dp register end********************************************/ + +#endif /* __PHYTIUM_REG_H__ */ diff --git a/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/px210_dc.c b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/px210_dc.c new file mode 100644 index 000000000..5c55f223e --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/px210_dc.c @@ -0,0 +1,326 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Phytium display drm driver + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include "phytium_display_drv.h" +#include "px210_reg.h" +#include "phytium_crtc.h" +#include "phytium_plane.h" +#include "phytium_fb.h" +#include "phytium_gem.h" + +static const unsigned int px210_primary_formats[] = { + DRM_FORMAT_ARGB2101010, + DRM_FORMAT_ABGR2101010, + DRM_FORMAT_RGBA1010102, + DRM_FORMAT_BGRA1010102, + DRM_FORMAT_ARGB8888, + DRM_FORMAT_ABGR8888, + DRM_FORMAT_RGBA8888, + DRM_FORMAT_BGRA8888, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_XBGR8888, + DRM_FORMAT_RGBX8888, + DRM_FORMAT_BGRX8888, + DRM_FORMAT_ARGB4444, + DRM_FORMAT_ABGR4444, + DRM_FORMAT_RGBA4444, + DRM_FORMAT_BGRA4444, + DRM_FORMAT_XRGB4444, + DRM_FORMAT_XBGR4444, + DRM_FORMAT_RGBX4444, + DRM_FORMAT_BGRX4444, + DRM_FORMAT_ARGB1555, + DRM_FORMAT_ABGR1555, + DRM_FORMAT_RGBA5551, + DRM_FORMAT_BGRA5551, + DRM_FORMAT_XRGB1555, + DRM_FORMAT_XBGR1555, + DRM_FORMAT_RGBX5551, + DRM_FORMAT_BGRX5551, + DRM_FORMAT_RGB565, + DRM_FORMAT_BGR565, + DRM_FORMAT_YUYV, + DRM_FORMAT_UYVY, +}; + +static uint64_t px210_primary_formats_modifiers[] = { + DRM_FORMAT_MOD_LINEAR, + DRM_FORMAT_MOD_PHYTIUM_TILE_MODE0_FBCDC, + DRM_FORMAT_MOD_PHYTIUM_TILE_MODE3_FBCDC, + DRM_FORMAT_MOD_INVALID +}; + +static uint64_t px210_cursor_formats_modifiers[] = { + DRM_FORMAT_MOD_LINEAR, + DRM_FORMAT_MOD_INVALID +}; + +static const unsigned int px210_cursor_formats[] = { + DRM_FORMAT_ARGB8888, +}; + +void px210_dc_hw_vram_init(struct phytium_display_private *priv, resource_size_t vram_addr, + resource_size_t vram_size) +{ + uint32_t config; + uint32_t group_offset = priv->address_transform_base; + + config = phytium_readl_reg(priv, group_offset, + PX210_GPU_ADDRESS_TRANSFORM_SRC_ADDR); + if (config) + phytium_writel_reg(priv, config, group_offset, + PX210_GPU_ADDRESS_TRANSFORM_SRC_ADDR); + + config = phytium_readl_reg(priv, group_offset, + PX210_GPU_ADDRESS_TRANSFORM_SIZE); + if (config) + phytium_writel_reg(priv, config, group_offset, + PX210_GPU_ADDRESS_TRANSFORM_SIZE); + + config = phytium_readl_reg(priv, group_offset, + PX210_GPU_ADDRESS_TRANSFORM_DST_ADDR); + if (config) + phytium_writel_reg(priv, config, group_offset, + PX210_GPU_ADDRESS_TRANSFORM_DST_ADDR); + + phytium_writel_reg(priv, (vram_addr & SRC_ADDR_MASK) >> SRC_ADDR_OFFSET, + group_offset, PX210_DC_ADDRESS_TRANSFORM_SRC_ADDR); + phytium_writel_reg(priv, (vram_size >> SIZE_OFFSET) | ADDRESS_TRANSFORM_ENABLE, + group_offset, PX210_DC_ADDRESS_TRANSFORM_SIZE); + config = phytium_readl_reg(priv, group_offset, PX210_DC_ADDRESS_TRANSFORM_DST_ADDR); + phytium_writel_reg(priv, config, group_offset, PX210_DC_ADDRESS_TRANSFORM_DST_ADDR); +} + +void px210_dc_hw_clear_msi_irq(struct phytium_display_private *priv, uint32_t phys_pipe) +{ + phytium_writel_reg(priv, MSI_CLEAR, priv->dcreq_reg_base[phys_pipe], PX210_DCREQ_MSI_CLEAR); +} + +void px210_dc_hw_config_pix_clock(struct drm_crtc *crtc, int clock) +{ + struct drm_device *dev = crtc->dev; + struct phytium_display_private *priv = dev->dev_private; + struct phytium_crtc *phytium_crtc = to_phytium_crtc(crtc); + int phys_pipe = phytium_crtc->phys_pipe; + uint32_t group_offset = priv->dcreq_reg_base[phys_pipe]; + int ret = 0; + + /* config pix clock */ + phytium_writel_reg(priv, FLAG_REQUEST | CMD_PIXEL_CLOCK | (clock & PIXEL_CLOCK_MASK), + group_offset, PX210_DCREQ_CMD_REGISTER); + ret = phytium_wait_cmd_done(priv, group_offset + PX210_DCREQ_CMD_REGISTER, + FLAG_REQUEST, FLAG_REPLY); + if (ret < 0) + DRM_ERROR("%s: failed to set pixel clock\n", __func__); +} + +void px210_dc_hw_disable(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct phytium_display_private *priv = dev->dev_private; + struct phytium_crtc *phytium_crtc = to_phytium_crtc(crtc); + int reset_timeout = 100; + int config = 0; + int phys_pipe = phytium_crtc->phys_pipe; + + // reset dc + config = phytium_readl_reg(priv, priv->dc_reg_base[phys_pipe], PX210_DC_CLOCK_CONTROL); + phytium_writel_reg(priv, config | SOFT_RESET, priv->dc_reg_base[phys_pipe], + PX210_DC_CLOCK_CONTROL); + phytium_writel_reg(priv, 0, priv->dc_reg_base[phys_pipe], PX210_DC_CLOCK_CONTROL); + do { + config = phytium_readl_reg(priv, priv->dc_reg_base[phys_pipe], PX210_DC_CLOCK_IDLE); + if (config | IS_IDLE) + break; + mdelay(1); + reset_timeout--; + } while (reset_timeout); + + /* reset pix clock */ + px210_dc_hw_config_pix_clock(crtc, 0); + + // reset dc + reset_timeout = 100; + config = phytium_readl_reg(priv, priv->dc_reg_base[phys_pipe], PX210_DC_CLOCK_CONTROL); + phytium_writel_reg(priv, config | SOFT_RESET, priv->dc_reg_base[phys_pipe], + PX210_DC_CLOCK_CONTROL); + phytium_writel_reg(priv, 0, priv->dc_reg_base[phys_pipe], PX210_DC_CLOCK_CONTROL); + do { + config = phytium_readl_reg(priv, priv->dc_reg_base[phys_pipe], PX210_DC_CLOCK_IDLE); + if (config | IS_IDLE) + break; + mdelay(1); + reset_timeout--; + } while (reset_timeout); + + /* reset dcreq */ + phytium_writel_reg(priv, DCREQ_PLAN_A, priv->dcreq_reg_base[phys_pipe], PX210_DCREQ_PLAN); + phytium_writel_reg(priv, 0, priv->dcreq_reg_base[phys_pipe], PX210_DCREQ_CONTROL); + phytium_writel_reg(priv, DCREQ_RESET, priv->dcreq_reg_base[phys_pipe], PX210_DCREQ_RESET); + msleep(20); + phytium_writel_reg(priv, (~DCREQ_RESET)&DCREQ_RESET_MASK, + priv->dcreq_reg_base[phys_pipe], PX210_DCREQ_RESET); +} + +int px210_dc_hw_fb_format_check(const struct drm_mode_fb_cmd2 *mode_cmd, int count) +{ + int ret = 0; + + switch (mode_cmd->modifier[count]) { + case DRM_FORMAT_MOD_PHYTIUM_TILE_MODE0_FBCDC: + switch (mode_cmd->pixel_format) { + case DRM_FORMAT_ARGB4444: + case DRM_FORMAT_ABGR4444: + case DRM_FORMAT_RGBA4444: + case DRM_FORMAT_BGRA4444: + case DRM_FORMAT_XRGB4444: + case DRM_FORMAT_XBGR4444: + case DRM_FORMAT_RGBX4444: + case DRM_FORMAT_BGRX4444: + case DRM_FORMAT_ARGB1555: + case DRM_FORMAT_ABGR1555: + case DRM_FORMAT_RGBA5551: + case DRM_FORMAT_BGRA5551: + case DRM_FORMAT_XRGB1555: + case DRM_FORMAT_XBGR1555: + case DRM_FORMAT_RGBX5551: + case DRM_FORMAT_BGRX5551: + case DRM_FORMAT_RGB565: + case DRM_FORMAT_BGR565: + case DRM_FORMAT_YUYV: + case DRM_FORMAT_UYVY: + break; + default: + DRM_ERROR("TILE_MODE0_FBCDC not support DRM_FORMAT %d", + mode_cmd->pixel_format); + ret = -EINVAL; + goto error; + } + break; + case DRM_FORMAT_MOD_PHYTIUM_TILE_MODE3_FBCDC: + switch (mode_cmd->pixel_format) { + case DRM_FORMAT_ARGB2101010: + case DRM_FORMAT_ABGR2101010: + case DRM_FORMAT_RGBA1010102: + case DRM_FORMAT_BGRA1010102: + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_ABGR8888: + case DRM_FORMAT_RGBA8888: + case DRM_FORMAT_BGRA8888: + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_RGBX8888: + case DRM_FORMAT_BGRX8888: + break; + default: + DRM_ERROR("TILE_MODE3_FBCDC not support DRM_FORMAT %d", + mode_cmd->pixel_format); + ret = -EINVAL; + goto error; + } + break; + case DRM_FORMAT_MOD_LINEAR: + break; + default: + DRM_ERROR("unsupported fb modifier 0x%llx\n", mode_cmd->modifier[0]); + ret = -EINVAL; + goto error; + } + + return 0; +error: + return ret; +} + +void px210_dc_hw_plane_get_primary_format(const uint64_t **format_modifiers, + const uint32_t **formats, + uint32_t *format_count) +{ + *format_modifiers = px210_primary_formats_modifiers; + *formats = px210_primary_formats; + *format_count = ARRAY_SIZE(px210_primary_formats); +} + +void px210_dc_hw_plane_get_cursor_format(const uint64_t **format_modifiers, + const uint32_t **formats, + uint32_t *format_count) +{ + *format_modifiers = px210_cursor_formats_modifiers; + *formats = px210_cursor_formats; + *format_count = ARRAY_SIZE(px210_cursor_formats); +} + +void px210_dc_hw_update_dcreq(struct drm_plane *plane) +{ + struct drm_device *dev = plane->dev; + struct phytium_display_private *priv = dev->dev_private; + struct phytium_plane *phytium_plane = to_phytium_plane(plane); + int phys_pipe = phytium_plane->phys_pipe; + uint32_t group_offset = priv->dcreq_reg_base[phys_pipe]; + int config; + + if (phytium_plane->tiling[0] == FRAMEBUFFER_LINEAR) { + phytium_writel_reg(priv, DCREQ_MODE_LINEAR, + group_offset, PX210_DCREQ_PLANE0_CONFIG); + } else { + config = DCREQ_NO_LOSSY; + if (phytium_plane->tiling[0] == FRAMEBUFFER_TILE_MODE0) + config |= DCREQ_TILE_TYPE_MODE0; + else if (phytium_plane->tiling[0] == FRAMEBUFFER_TILE_MODE3) + config |= DCREQ_TILE_TYPE_MODE3; + else + config |= DCREQ_TILE_TYPE_MODE0; + + switch (phytium_plane->format) { + case FRAMEBUFFER_FORMAT_ARGB8888: + case FRAMEBUFFER_FORMAT_XRGB8888: + config |= DCREQ_COLOURFORMAT_BGRA8888; + break; + case FRAMEBUFFER_FORMAT_ARGB2101010: + config |= DCREQ_COLOURFORMAT_ARGB2101010; + break; + case FRAMEBUFFER_FORMAT_XRGB4444: + case FRAMEBUFFER_FORMAT_ARGB4444: + config |= DCREQ_COLOURFORMAT_ARGB4444; + break; + case FRAMEBUFFER_FORMAT_XRGB1555: + case FRAMEBUFFER_FORMAT_ARGB1555: + config |= DCREQ_COLOURFORMAT_ARGB1555; + break; + case FRAMEBUFFER_FORMAT_RGB565: + config |= DCREQ_COLOURFORMAT_RGB565; + break; + case FRAMEBUFFER_FORMAT_YUYV: + config |= DCREQ_COLOURFORMAT_YUYV; + break; + case FRAMEBUFFER_FORMAT_UYVY: + config |= DCREQ_COLOURFORMAT_UYVY; + break; + } + config |= DCREQ_ARGBSWIZZLE_ARGB; + config |= DCREQ_MODE_TILE; + phytium_writel_reg(priv, phytium_plane->iova[0] & 0xffffffff, + group_offset, PX210_DCREQ_PLANE0_ADDR_START); + phytium_writel_reg(priv, (phytium_plane->iova[0] + phytium_plane->size[0]) & + 0xffffffff, group_offset, PX210_DCREQ_PLANE0_ADDR_END); + phytium_writel_reg(priv, config, group_offset, PX210_DCREQ_PLANE0_CONFIG); + } +} + +void px210_dc_hw_update_primary_hi_addr(struct drm_plane *plane) +{ + struct drm_device *dev = plane->dev; + struct phytium_display_private *priv = dev->dev_private; + struct phytium_plane *phytium_plane = to_phytium_plane(plane); + int phys_pipe = phytium_plane->phys_pipe; + + phytium_writel_reg(priv, (phytium_plane->iova[0] >> PREFIX_SHIFT) & PREFIX_MASK, + priv->dcreq_reg_base[phys_pipe], PX210_DCREQ_PIX_DMA_PREFIX); +} diff --git a/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/px210_dc.h b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/px210_dc.h new file mode 100644 index 000000000..acb2dd6cb --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/px210_dc.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Phytium display drm driver + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#ifndef __PX210_DC_H__ +#define __PX210_DC_H__ + +#define PX210_DC_PIX_CLOCK_MAX (594000) +#define PX210_DC_HDISPLAY_MAX 3840 +#define PX210_DC_VDISPLAY_MAX 2160 +#define PX210_DC_ADDRESS_MASK 0x7f + +extern void px210_dc_hw_vram_init(struct phytium_display_private *priv, + resource_size_t vram_addr, + resource_size_t vram_size); +extern void px210_dc_hw_clear_msi_irq(struct phytium_display_private *priv, uint32_t phys_pipe); +extern void px210_dc_hw_config_pix_clock(struct drm_crtc *crtc, int clock); +extern void px210_dc_hw_disable(struct drm_crtc *crtc); +extern int px210_dc_hw_fb_format_check(const struct drm_mode_fb_cmd2 *mode_cmd, int count); +extern void px210_dc_hw_plane_get_primary_format(const uint64_t **format_modifiers, + const uint32_t **formats, + uint32_t *format_count); +extern void px210_dc_hw_plane_get_cursor_format(const uint64_t **format_modifiers, + const uint32_t **formats, + uint32_t *format_count); +void px210_dc_hw_update_dcreq(struct drm_plane *plane); +void px210_dc_hw_update_primary_hi_addr(struct drm_plane *plane); +#endif /* __PX210_DC_H__ */ diff --git a/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/px210_dp.c b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/px210_dp.c new file mode 100644 index 000000000..d7bd04eac --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/px210_dp.c @@ -0,0 +1,920 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Phytium display drm driver + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#include "phytium_display_drv.h" +#include "px210_reg.h" +#include "phytium_dp.h" +#include "px210_dp.h" + +static uint8_t px210_dp_source_lane_count[3] = {4, 4, 1}; + +/* [reg][ling_rate 1.62->8.1] */ +static int vco_val[12][4] = { + {0x0509, 0x0509, 0x0509, 0x0509}, // CP_PADJ + {0x0f00, 0x0f00, 0x0f00, 0x0f00}, // CP_IADJ + {0x0F08, 0x0F08, 0x0F08, 0x0F08}, // FILT_PADJ + {0x0061, 0x006C, 0x006C, 0x0051}, // INTDIV + {0x3333, 0x0000, 0x0000, 0x0000}, // FRACDIVL + {0x0000, 0x0000, 0x0000, 0x0000}, // FRACDIVH + {0x0042, 0x0048, 0x0048, 0x0036}, // HIGH_THR + {0x0002, 0x0002, 0x0002, 0x0002}, // PDIAG_CTRL + {0x0c5e, 0x0c5e, 0x0c5e, 0x0c5e}, // VCOCAL_PLLCNT_START + {0x00c7, 0x00c7, 0x00c7, 0x00c7}, // LOCK_PEFCNT + {0x00c7, 0x00c7, 0x00c7, 0x00c7}, // LOCK_PLLCNT_START + {0x0005, 0x0005, 0x0005, 0x0005}, // LOCK_PLLCNT_THR +}; + +static int mgnfs_val[4][4][4] = // [link_rate][swing][emphasis] +{ + /* 1.62Gbps */ + { + {0x0026, 0x001f, 0x0012, 0x0000}, + {0x0013, 0x0013, 0x0000, 0x0000}, + {0x0006, 0x0000, 0x0000, 0x0000}, + {0x0000, 0x0000, 0x0000, 0x0000}, + }, + + /* 2.7Gbps */ + { + {0x0026, 0x001f, 0x0012, 0x0000}, + {0x0013, 0x0013, 0x0000, 0x0000}, + {0x0006, 0x0000, 0x0000, 0x0000}, + {0x0000, 0x0000, 0x0000, 0x0000}, + }, + + /* 5.4Gbps */ + { + {0x0026, 0x0013, 0x005, 0x0000}, + {0x0018, 0x006, 0x0000, 0x0000}, + {0x000c, 0x0000, 0x0000, 0x0000}, + {0x0000, 0x0000, 0x0000, 0x0000}, + }, + + /* 8.1Gbps */ + { + {0x0026, 0x0013, 0x005, 0x0000}, + {0x0013, 0x006, 0x0000, 0x0000}, + {0x0006, 0x0000, 0x0000, 0x0000}, + {0x0000, 0x0000, 0x0000, 0x0000}, + }, +}; + +static int cpost_val[4][4][4] = // [link_rate][swing][emphasis] +{ + /* 1.62Gbps */ + { + {0x0000, 0x0014, 0x0020, 0x002a}, + {0x0000, 0x0010, 0x001f, 0x0000}, + {0x0000, 0x0013, 0x0000, 0x0000}, + {0x0000, 0x0000, 0x0000, 0x0000}, + }, + + /* 2.7Gbps */ + { + {0x0000, 0x0014, 0x0020, 0x002a}, + {0x0000, 0x0010, 0x001f, 0x0000}, + {0x0000, 0x0013, 0x0000, 0x0000}, + {0x0000, 0x0000, 0x0000, 0x0000}, + }, + + /* 5.4Gbps */ + { + {0x0000, 0x0014, 0x0022, 0x002e}, + {0x0000, 0x0013, 0x0020, 0x0000}, + {0x0000, 0x0013, 0x0000, 0x0000}, + {0x0000, 0x0000, 0x0000, 0x0000}, + }, + + /* 8.1Gbps */ + { + {0x0000, 0x0014, 0x0022, 0x002e}, + {0x0000, 0x0013, 0x0020, 0x0000}, + {0x0000, 0x0013, 0x0000, 0x0000}, + {0x0000, 0x0000, 0x0000, 0x0000}, + }, +}; + +static int px210_dp_hw_set_phy_lane_and_rate(struct phytium_dp_device *phytium_dp, + uint8_t link_lane_count, + uint32_t link_rate) +{ + int port = phytium_dp->port%3; + int i = 0, data, tmp, tmp1, index = 0, mask; + int timeout = 500, ret = 0; + + if (port == 0 || port == 1) { + /* set pma powerdown */ + data = 0; + mask = 0; + for (i = 0; i < phytium_dp->source_max_lane_count; i++) { + data |= (A3_POWERDOWN3 << i*A3_POWERDOWN3_SHIFT); + mask |= (((1<source_max_lane_count; i++) { + data |= (PLL_EN << i*PLL_EN_SHIFT); + mask |= (((1<source_max_lane_count; i++) { + data |= (PLL_EN << i*PLL_EN_SHIFT); + mask |= (((1<source_max_lane_count; i++) { + data |= (A0_ACTIVE << i*A0_ACTIVE_SHIFT); + mask |= (((1<port%3; + int voltage_swing = 0; + int pre_emphasis = 0, link_rate_index = 0; + + switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) { + case DP_TRAIN_VOLTAGE_SWING_LEVEL_0: + default: + voltage_swing = 0; + break; + case DP_TRAIN_VOLTAGE_SWING_LEVEL_1: + voltage_swing = 1; + break; + case DP_TRAIN_VOLTAGE_SWING_LEVEL_2: + voltage_swing = 2; + break; + case DP_TRAIN_VOLTAGE_SWING_LEVEL_3: + voltage_swing = 3; + break; + } + switch (train_set & DP_TRAIN_PRE_EMPHASIS_MASK) { + case DP_TRAIN_PRE_EMPH_LEVEL_0: + default: + pre_emphasis = 0; + break; + case DP_TRAIN_PRE_EMPH_LEVEL_1: + pre_emphasis = 1; + break; + case DP_TRAIN_PRE_EMPH_LEVEL_2: + pre_emphasis = 2; + break; + case DP_TRAIN_PRE_EMPH_LEVEL_3: + pre_emphasis = 3; + break; + } + + switch (link_rate) { + case 810000: + link_rate_index = 3; + break; + case 540000: + link_rate_index = 2; + break; + case 270000: + link_rate_index = 1; + break; + case 162000: + link_rate_index = 0; + break; + default: + DRM_ERROR("phytium dp rate(%d) not support\n", link_rate); + link_rate_index = 2; + break; + } + + if (port == 0) { + phytium_phy_writel(phytium_dp, PX210_PHY0_PLL0_TX_DIAG_ACYA, LOCK); + phytium_phy_writel(phytium_dp, PX210_PHY0_PLL0_TX_TXCC_CTRL, TX_TXCC_CTRL); + phytium_phy_writel(phytium_dp, PX210_PHY0_PLL0_TX_DRV, TX_DRV); + phytium_phy_writel(phytium_dp, PX210_PHY0_PLL0_TX_MGNFS, + mgnfs_val[link_rate_index][voltage_swing][pre_emphasis]); + phytium_phy_writel(phytium_dp, PX210_PHY0_PLL0_TX_CPOST, + cpost_val[link_rate_index][voltage_swing][pre_emphasis]); + phytium_phy_writel(phytium_dp, PX210_PHY0_PLL0_TX_DIAG_ACYA, UNLOCK); + + } else if (port == 1) { + phytium_phy_writel(phytium_dp, PX210_PHY0_PLL1_TX_DIAG_ACYA, LOCK); + phytium_phy_writel(phytium_dp, PX210_PHY0_PLL1_TX_TXCC_CTRL, TX_TXCC_CTRL); + phytium_phy_writel(phytium_dp, PX210_PHY0_PLL1_TX_DRV, TX_DRV); + phytium_phy_writel(phytium_dp, PX210_PHY0_PLL1_TX_MGNFS, + mgnfs_val[link_rate_index][voltage_swing][pre_emphasis]); + phytium_phy_writel(phytium_dp, PX210_PHY0_PLL1_TX_CPOST, + cpost_val[link_rate_index][voltage_swing][pre_emphasis]); + phytium_phy_writel(phytium_dp, PX210_PHY0_PLL1_TX_CPOST1, + cpost_val[link_rate_index][voltage_swing][pre_emphasis]); + phytium_phy_writel(phytium_dp, PX210_PHY0_PLL1_TX_DIAG_ACYA, UNLOCK); + } else { + phytium_phy_writel(phytium_dp, PX210_PHY1_PLL0_TX_DIAG_ACYA, LOCK); + phytium_phy_writel(phytium_dp, PX210_PHY1_PLL0_TX_TXCC_CTRL, TX_TXCC_CTRL); + phytium_phy_writel(phytium_dp, PX210_PHY1_PLL0_TX_DRV, TX_DRV); + phytium_phy_writel(phytium_dp, PX210_PHY1_PLL0_TX_MGNFS, + mgnfs_val[link_rate_index][voltage_swing][pre_emphasis]); + phytium_phy_writel(phytium_dp, PX210_PHY1_PLL0_TX_CPOST, + cpost_val[link_rate_index][voltage_swing][pre_emphasis]); + phytium_phy_writel(phytium_dp, PX210_PHY1_PLL0_TX_DIAG_ACYA, UNLOCK); + } +} + +static int px210_dp_hw_init_phy(struct phytium_dp_device *phytium_dp) +{ + int port = phytium_dp->port; + int i = 0, data, tmp, mask; + int timeout = 500, ret = 0; + + if (port == 0 || port == 1) { + phytium_phy_writel(phytium_dp, PX210_PHY0_APB_RESET, APB_RESET); + + phytium_phy_writel(phytium_dp, PX210_PHY0_PIPE_RESET, RESET); + + /* config lane to dp mode */ + data = 0; + mask = 0; + for (i = 0; i < phytium_dp->source_max_lane_count; i++) { + data |= (LANE_BIT << i*LANE_BIT_SHIFT); + mask |= (((1<source_max_lane_count; i++) { + data |= (LANE_MASTER << i*LANE_MASTER_SHIFT); + mask |= (((1<source_max_lane_count; i++) { + data |= (PLL_EN << i*PLL_EN_SHIFT); + mask |= (((1<source_max_lane_count; i++) { + data |= (BIT_20 << i*BIT_20_SHIFT); + mask |= (((1<source_max_lane_count; i++) { + data |= (A0_ACTIVE << i*A0_ACTIVE_SHIFT); + mask |= (((1<dev; + struct phytium_display_private *priv = dev->dev_private; + int port = phytium_dp->port; + uint32_t group_offset = priv->dcreq_reg_base[port]; + int ret = 0; + + phytium_writel_reg(priv, FLAG_REQUEST | CMD_BACKLIGHT | PANEL_POWER_ENABLE, + group_offset, PX210_DCREQ_CMD_REGISTER); + ret = phytium_wait_cmd_done(priv, group_offset + PX210_DCREQ_CMD_REGISTER, + FLAG_REQUEST, FLAG_REPLY); + if (ret < 0) + DRM_ERROR("%s: failed to poweron panel\n", __func__); +} + +static void px210_dp_hw_poweroff_panel(struct phytium_dp_device *phytium_dp) +{ + struct drm_device *dev = phytium_dp->dev; + struct phytium_display_private *priv = dev->dev_private; + int port = phytium_dp->port; + uint32_t group_offset = priv->dcreq_reg_base[port]; + int ret = 0; + + phytium_writel_reg(priv, FLAG_REQUEST | CMD_BACKLIGHT | PANEL_POWER_DISABLE, + group_offset, PX210_DCREQ_CMD_REGISTER); + ret = phytium_wait_cmd_done(priv, group_offset + PX210_DCREQ_CMD_REGISTER, + FLAG_REQUEST, FLAG_REPLY); + if (ret < 0) + DRM_ERROR("%s: failed to poweroff panel\n", __func__); +} + +static void px210_dp_hw_enable_backlight(struct phytium_dp_device *phytium_dp) +{ + struct drm_device *dev = phytium_dp->dev; + struct phytium_display_private *priv = dev->dev_private; + int port = phytium_dp->port, ret = 0; + uint32_t group_offset = priv->dcreq_reg_base[port]; + + phytium_writel_reg(priv, FLAG_REQUEST | CMD_BACKLIGHT | BACKLIGHT_ENABLE, + group_offset, PX210_DCREQ_CMD_REGISTER); + ret = phytium_wait_cmd_done(priv, group_offset + PX210_DCREQ_CMD_REGISTER, + FLAG_REQUEST, FLAG_REPLY); + if (ret < 0) + DRM_ERROR("%s: failed to enable backlight\n", __func__); +} + +static void px210_dp_hw_disable_backlight(struct phytium_dp_device *phytium_dp) +{ + struct drm_device *dev = phytium_dp->dev; + struct phytium_display_private *priv = dev->dev_private; + int port = phytium_dp->port; + uint32_t group_offset = priv->dcreq_reg_base[port]; + int ret = 0; + + phytium_writel_reg(priv, FLAG_REQUEST | CMD_BACKLIGHT | BACKLIGHT_DISABLE, + group_offset, PX210_DCREQ_CMD_REGISTER); + ret = phytium_wait_cmd_done(priv, group_offset + PX210_DCREQ_CMD_REGISTER, + FLAG_REQUEST, FLAG_REPLY); + if (ret < 0) + DRM_ERROR("%s: failed to disable backlight\n", __func__); +} + +static uint32_t px210_dp_hw_get_backlight(struct phytium_dp_device *phytium_dp) +{ + struct drm_device *dev = phytium_dp->dev; + struct phytium_display_private *priv = dev->dev_private; + int config; + uint32_t group_offset = priv->address_transform_base; + + config = phytium_readl_reg(priv, group_offset, PX210_DC_ADDRESS_TRANSFORM_BACKLIGHT_VALUE); + return ((config >> BACKLIGHT_VALUE_SHIFT) & BACKLIGHT_VALUE_MASK); +} + +static int px210_dp_hw_set_backlight(struct phytium_dp_device *phytium_dp, uint32_t level) +{ + struct drm_device *dev = phytium_dp->dev; + struct phytium_display_private *priv = dev->dev_private; + int port = phytium_dp->port; + uint32_t group_offset = priv->dcreq_reg_base[port]; + int config = 0; + int ret = 0; + + if (level > PX210_DP_BACKLIGHT_MAX) { + ret = -EINVAL; + goto out; + } + + config = FLAG_REQUEST | CMD_BACKLIGHT | ((level & BACKLIGHT_MASK) << BACKLIGHT_SHIFT); + phytium_writel_reg(priv, config, group_offset, PX210_DCREQ_CMD_REGISTER); + ret = phytium_wait_cmd_done(priv, group_offset + PX210_DCREQ_CMD_REGISTER, + FLAG_REQUEST, FLAG_REPLY); + if (ret < 0) + DRM_ERROR("%s: failed to set backlight\n", __func__); + +out: + return ret; +} + +bool px210_dp_hw_spread_is_enable(struct phytium_dp_device *phytium_dp) +{ + struct drm_device *dev = phytium_dp->dev; + struct phytium_display_private *priv = dev->dev_private; + int port = phytium_dp->port, config; + uint32_t group_offset = priv->address_transform_base; + + config = phytium_readl_reg(priv, group_offset, PX210_DC_ADDRESS_TRANSFORM_DP_RESET_STATUS); + + return ((config & DP_SPREAD_ENABLE(port)) ? true:false); +} + +int px210_dp_hw_reset(struct phytium_dp_device *phytium_dp) +{ + struct drm_device *dev = phytium_dp->dev; + struct phytium_display_private *priv = dev->dev_private; + int port = phytium_dp->port; + int timeout = 100, config, ret = 0; + uint32_t group_offset = priv->address_transform_base; + uint32_t group_offset_dp = priv->dp_reg_base[port]; + + config = phytium_readl_reg(priv, group_offset, PX210_DC_ADDRESS_TRANSFORM_DP_RESET_STATUS); + config &= (~DC_DP_RESET_STATUS(port)); + + phytium_writel_reg(priv, config, group_offset, PX210_DC_ADDRESS_TRANSFORM_DP_RESET_STATUS); + phytium_writel_reg(priv, FLAG_REQUEST | CMD_DC_DP_RESET, + priv->dcreq_reg_base[port], PX210_DCREQ_CMD_REGISTER); + do { + mdelay(10); + timeout--; + config = phytium_readl_reg(priv, group_offset, + PX210_DC_ADDRESS_TRANSFORM_DP_RESET_STATUS); + if (config & DC_DP_RESET_STATUS(port)) + break; + } while (timeout); + if (timeout == 0) { + DRM_ERROR("reset dc/dp pipe(%d) failed\n", port); + ret = -1; + } + + phytium_writel_reg(priv, AUX_CLK_DIVIDER, group_offset_dp, PHYTIUM_DP_AUX_CLK_DIVIDER); + + return ret; +} + +uint8_t px210_dp_hw_get_source_lane_count(struct phytium_dp_device *phytium_dp) +{ + return px210_dp_source_lane_count[phytium_dp->port]; +} + +static struct phytium_dp_func px210_dp_funcs = { + .dp_hw_get_source_lane_count = px210_dp_hw_get_source_lane_count, + .dp_hw_reset = px210_dp_hw_reset, + .dp_hw_spread_is_enable = px210_dp_hw_spread_is_enable, + .dp_hw_set_backlight = px210_dp_hw_set_backlight, + .dp_hw_get_backlight = px210_dp_hw_get_backlight, + .dp_hw_disable_backlight = px210_dp_hw_disable_backlight, + .dp_hw_enable_backlight = px210_dp_hw_enable_backlight, + .dp_hw_poweroff_panel = px210_dp_hw_poweroff_panel, + .dp_hw_poweron_panel = px210_dp_hw_poweron_panel, + .dp_hw_init_phy = px210_dp_hw_init_phy, + .dp_hw_set_phy_lane_setting = px210_dp_hw_set_phy_lane_setting, + .dp_hw_set_phy_lane_and_rate = px210_dp_hw_set_phy_lane_and_rate, +}; + +void px210_dp_func_register(struct phytium_dp_device *phytium_dp) +{ + phytium_dp->funcs = &px210_dp_funcs; +} diff --git a/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/px210_dp.h b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/px210_dp.h new file mode 100644 index 000000000..4ad65397f --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/px210_dp.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Phytium display drm driver + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#ifndef __PX210_DP_H__ +#define __PX210_DP_H__ + +#define PX210_DP_BACKLIGHT_MAX 100 + +void px210_dp_func_register(struct phytium_dp_device *phytium_dp); +#endif /* __PX210_DP_H__ */ diff --git a/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/px210_reg.h b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/px210_reg.h new file mode 100644 index 000000000..5556b3ee5 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/gpu/drm/phytium/px210_reg.h @@ -0,0 +1,349 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Phytium display drm driver + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#ifndef __PX210_REG_H__ +#define __PX210_REG_H__ + +#include "phytium_reg.h" + +/******************************dc register start******************************************/ +#define PX210_DC_CLOCK_CONTROL 0x0000 + #define SOFT_RESET (1<<12) +#define PX210_DC_CLOCK_IDLE 0x0004 + #define IS_IDLE (1<<16) +/******************************dc register end********************************************/ + +/******************************dcreq register start**************************************/ +#define PX210_DCREQ_PLANE0_ADDR_START 0x00 +#define PX210_DCREQ_PLANE0_ADDR_END 0x04 +#define PX210_DCREQ_PLANE1_ADDR_START 0x08 +#define PX210_DCREQ_PLANE1_ADDR_END 0x0c +#define PX210_DCREQ_PLANE0_CONFIG 0x10 + #define DCREQ_NO_LOSSY (0 << 0) + #define DCREQ_LOSSY (1 << 0) + #define DCREQ_TILE_TYPE_MASK (0x3 << 1) + #define DCREQ_TILE_TYPE_MODE0 (0x1 << 1) + #define DCREQ_TILE_TYPE_MODE3 (0x2 << 1) + #define DCREQ_COLOURFORMAT_MASK (0x7f << 8) + #define DCREQ_COLOURFORMAT_RGB565 (0x5 << 8) + #define DCREQ_COLOURFORMAT_ARGB1555 (0x4 << 8) + #define DCREQ_COLOURFORMAT_ARGB4444 (0x02 << 8) + #define DCREQ_COLOURFORMAT_BGRA8888 (0x29 << 8) + #define DCREQ_COLOURFORMAT_ARGB2101010 (0xe << 8) + #define DCREQ_COLOURFORMAT_YUYV (0x59 << 8) + #define DCREQ_COLOURFORMAT_UYVY (0x5b << 8) + #define DCREQ_ARGBSWIZZLE_MASK (0xf << 4) + #define DCREQ_ARGBSWIZZLE_ARGB (0X0 << 4) + #define DCREQ_ARGBSWIZZLE_BGRA (0XC << 4) + #define DCREQ_MODE_MASK (1 << 16) + #define DCREQ_MODE_LINEAR (0 << 16) + #define DCREQ_MODE_TILE (1 << 16) +#define PX210_DCREQ_PLANE1_CONFIG(pipe) 0x14 +#define PX210_DCREQ_PLANE0_CLEAR_COLOR_L 0x18 +#define PX210_DCREQ_PLANE0_CLEAR_COLOR_H 0x1C +#define PX210_DCREQ_PLANE1_CLEAR_COLOR_L 0x20 +#define PX210_DCREQ_PLANE1_CLEAR_COLOR_H 0x24 +#define PX210_DCREQ_CMD_REGISTER 0x38 + #define FLAG_REPLY (1<<31) + #define FLAG_REQUEST (1<<30) + #define CMD_PIXEL_CLOCK (0x0 << 28) + #define CMD_BACKLIGHT (0x1 << 28) + #define CMD_DC_DP_RESET (0x3 << 28) + #define BACKLIGHT_SHIFT 21 + #define BACKLIGHT_MASK 0x7f + #define BACKLIGHT_MAX 100 + #define BACKLIGHT_ENABLE (101 << BACKLIGHT_SHIFT) + #define BACKLIGHT_DISABLE (102 << BACKLIGHT_SHIFT) + #define PANEL_POWER_ENABLE (103 << BACKLIGHT_SHIFT) + #define PANEL_POWER_DISABLE (104 << BACKLIGHT_SHIFT) + #define PIXEL_CLOCK_MASK (0x1fffff) +#define PX210_DCREQ_FBCD_CLOCK_CONFIG 0x3c +#define PX210_DCREQ_PIX_DMA_PREFIX 0x50 + #define PREFIX_MASK 0xff + #define PREFIX_SHIFT 32 +#define PX210_DCREQ_FRAME_START 0x54 +#define PX210_DCREQ_FILTER_CONFIG 0x58 +#define PX210_DCREQ_CONTROL 0x5C + #define DC_REQ_ENABLE (1<<0) +#define PX210_DCREQ_MSI_CLEAR 0x60 + #define MSI_CLEAR 0x0 +#define PX210_DCREQ_RESET 0x68 + #define DCREQ_RESET (0x3 << 0) + #define DCREQ_RESET_MASK 0x3 +#define PX210_DCREQ_PLAN 0x94 + #define DCREQ_PLAN_A 0x0 + #define DCREQ_PLAN_B 0X5 +/******************************dcreq register end**************************************/ + +/******************************address transform register start**************************/ +#define PX210_GPU_ADDRESS_TRANSFORM_SRC_ADDR 0x0 +#define PX210_GPU_ADDRESS_TRANSFORM_SIZE 0x4 +#define PX210_GPU_ADDRESS_TRANSFORM_DST_ADDR 0x8 + +#define PX210_DC_ADDRESS_TRANSFORM_SRC_ADDR 0x24 + #define SRC_ADDR_OFFSET 22 + #define SRC_ADDR_MASK 0xffffffffff +#define PX210_DC_ADDRESS_TRANSFORM_SIZE 0x28 + #define ADDRESS_TRANSFORM_ENABLE (0x1 << 31) + #define SIZE_OFFSET 22 +#define PX210_DC_ADDRESS_TRANSFORM_DST_ADDR 0x2c + #define DST_ADDR_OFFSET 22 +#define PX210_DC_ADDRESS_TRANSFORM_DP_RESET_STATUS 0x48 + #define DC_DP_RESET_STATUS(pipe) (1 << pipe) + #define DP_SPREAD_ENABLE(pipe) (0x8 << pipe) +#define PX210_DC_ADDRESS_TRANSFORM_BACKLIGHT_VALUE 0x4c + #define BACKLIGHT_VALUE_MASK (0x7f) + #define BACKLIGHT_VALUE_SHIFT 16 +/******************************address transform register end**************************/ + +/******************************phy register start******************************************/ +/* self define */ +#define PX210_PHY0_PIPE_RESET 0x40104 + #define RESET 0x0 + #define RESET_DEASSERT 0x1 +#define PX210_PHY1_PIPE_RESET 0x100100 + #define PHY1_PIPE_RESET 0x0 + #define PHY1_PIPE_RESET_DEASSERT 0x4 + +#define PX210_PHY1_EN_REFCLK 0x100070 + +#define PX210_PHY0_MODE 0x40088 + #define LANE_BIT (0x3) + #define LANE_BIT_SHIFT 0x2 +#define PX210_PHY1_SEL 0x100004 + #define PHY1_DP_LANE_BIT 0x1 + #define PHY1_DP_LANE_BIT_SHIFT 2 + +#define PX210_PHY0_LINK_CFG 0x40044 + #define LANE_MASTER 0x1 + #define LANE_MASTER_SHIFT 1 + +#define PX210_PHY0_PLL_EN 0x40010 + #define PLL_EN 0x1 + #define PLL_EN_SHIFT 1 +#define PX210_PHY0_PMA_WIDTH 0x40020 + #define BIT_20 0x5 + #define BIT_20_SHIFT 4 + +#define PX210_PHY0_PMA0_POWER 0x40014 +#define PX210_PHY0_PMA1_POWER 0x40018 + #define A0_ACTIVE 0x1 + #define A0_ACTIVE_SHIFT 8 + #define A3_POWERDOWN3 0x8 + #define A3_POWERDOWN3_SHIFT 8 + +#define PX210_PHY1_PMA_MISC 0x1000a0 + #define PHY1_PLL_EN 0x1 + #define PHY1_PLL_EN_MASK 1 + #define PHY1_PLL_EN_SHIFT 8 + #define PHY1_BIT_20 0x5 + #define PHY1_BIT_20_SHIFT 9 + #define PHY1_A0_ACTIVE 0x1 + #define PHY1_A0_ACTIVE_SHIFT 2 + #define PHY1_A0_ACTIVE_MASK 0x3f + #define PHY1_A3_POWERDOWN3 0x8 + #define PHY1_A3_POWERDOWN3_MASK 0x3f + #define PHY1_A3_POWERDOWN3_SHIFT 2 + +#define PX210_PHY0_LINK_RESET 0x40108 + #define LINK_RESET 0x1 + #define LINK_RESET_MASK 0x1 + #define LINTK_RESET_SHIFT 0x1 + +#define PX210_PHY0_APB_RESET 0x40100 + #define APB_RESET 0x1 +#define PX210_PHY1_APB_RESET 0x100104 + #define PHY1_APB_RESET 0x4 + +/* phy origin register */ +#define PX210_PHY0_PLL_CFG 0x30038 +#define PX210_PHY1_PLL_CFG 0xb0038 + #define SINGLE_LINK 0x0 + #define DOUBLE_LINK 0x2 + +#define PX210_PHY0_PMA_CONTROL 0x3800c +#define PX210_PHY1_PMA_CONTROL 0xb800c + #define CONTROL_ENABLE 0x1 + #define CONTROL_ENABLE_MASK 0x1 + #define CONTROL_ENABLE_SHIFT 0x1 + +#define PX210_PHY0_PMA_CONTROL2 0x38004 +#define PX210_PHY1_PMA_CONTROL2 0xb8004 + #define PLL0_LOCK_DONE (0x1 << 6) + #define PLL1_LOCK_DONE (0x1 << 7) + +#define PX210_PHY0_PLL0_CLK_SEL 0X684 +#define PX210_PHY0_PLL1_CLK_SEL 0x704 +#define PX210_PHY1_PLL_CLK_SEL 0X80684 + #define PLL_LINK_RATE_162000 0xf01 + #define PLL_LINK_RATE_270000 0x701 + #define PLL_LINK_RATE_540000 0x301 + #define PLL_LINK_RATE_810000 0x200 + +#define PX210_PHY0_HSCLK0_SEL 0x18398 +#define PX210_PHY0_HSCLK1_SEL 0x1a398 +#define PX210_PHY1_HSCLK_SEL 0x90398 + #define HSCLK_LINK_0 0x0 + #define HSCLK_LINK_1 0x1 + +#define PX210_PHY0_HSCLK0_DIV 0x1839c +#define PX210_PHY0_HSCLK1_DIV 0x1a39c +#define PX210_PHY1_HSCLK_DIV 0x9039c + #define HSCLK_LINK_RATE_162000 0x2 + #define HSCLK_LINK_RATE_270000 0x1 + #define HSCLK_LINK_RATE_540000 0x0 + #define HSCLK_LINK_RATE_810000 0x0 + +#define PX210_PHY0_PLLDRC0_CTRL 0x18394 +#define PX210_PHY0_PLLDRC1_CTRL 0x1a394 +#define PX210_PHY1_PLLDRC_CTRL 0x90394 + #define PLLDRC_LINK0 0x1 + #define PLLDRC_LINK1 0x9 + +#define PX210_PHY0_PLL0_DSM_M0 0x250 +#define PX210_PHY1_PLL0_DSM_M0 0x80250 + #define PLL0_DSM_M0 0x4 +#define PX210_PHY0_PLL0_VCOCAL_START 0x218 +#define PX210_PHY1_PLL0_VCOCAL_START 0x80218 + #define PLL0_VCOCAL_START 0xc5e +#define PX210_PHY0_PLL0_VCOCAL_CTRL 0x208 +#define PX210_PHY1_PLL0_VCOCAL_CTRL 0x80208 + #define PLL0_VCOCAL_CTRL 0x3 + +#define PX210_PHY0_PLL1_DSM_M0 0x350 + #define PLL1_DSM_M0 0x4 +#define PX210_PHY0_PLL1_VCOCAL_START 0x318 + #define PLL1_VCOCAL_START 0xc5e +#define PX210_PHY0_PLL1_VCOCAL_CTRL 0x308 + #define PLL1_VCOCAL_CTRL 0x3 + +#define PX210_PHY0_PLL0_CP_PADJ 0x690 +#define PX210_PHY0_PLL0_CP_IADJ 0x694 +#define PX210_PHY0_PLL0_CP_FILT_PADJ 0x698 +#define PX210_PHY0_PLL0_INTDIV 0x240 +#define PX210_PHY0_PLL0_FRACDIVL 0x244 +#define PX210_PHY0_PLL0_FRACDIVH 0x248 +#define PX210_PHY0_PLL0_HIGH_THR 0x24c +#define PX210_PHY0_PLL0_PDIAG_CTRL 0x680 +#define PX210_PHY0_PLL0_VCOCAL_PLLCNT_START 0x220 +#define PX210_PHY0_PLL0_LOCK_PEFCNT 0x270 +#define PX210_PHY0_PLL0_LOCK_PLLCNT_START 0x278 +#define PX210_PHY0_PLL0_LOCK_PLLCNT_THR 0x27c + +#define PX210_PHY0_PLL1_CP_PADJ 0x710 +#define PX210_PHY0_PLL1_CP_IADJ 0x714 +#define PX210_PHY0_PLL1_CP_FILT_PADJ 0x718 +#define PX210_PHY0_PLL1_INTDIV 0x340 +#define PX210_PHY0_PLL1_FRACDIVL 0x344 +#define PX210_PHY0_PLL1_FRACDIVH 0x348 +#define PX210_PHY0_PLL1_HIGH_THR 0x34c +#define PX210_PHY0_PLL1_PDIAG_CTRL 0x700 +#define PX210_PHY0_PLL1_VCOCAL_PLLCNT_START 0x320 +#define PX210_PHY0_PLL1_LOCK_PEFCNT 0x370 +#define PX210_PHY0_PLL1_LOCK_PLLCNT_START 0x378 +#define PX210_PHY0_PLL1_LOCK_PLLCNT_THR 0x37c + +#define PX210_PHY1_PLL0_CP_PADJ 0x80690 +#define PX210_PHY1_PLL0_CP_IADJ 0x80694 +#define PX210_PHY1_PLL0_CP_FILT_PADJ 0x80698 +#define PX210_PHY1_PLL0_INTDIV 0x80240 +#define PX210_PHY1_PLL0_FRACDIVL 0x80244 +#define PX210_PHY1_PLL0_FRACDIVH 0x80248 +#define PX210_PHY1_PLL0_HIGH_THR 0x8024c +#define PX210_PHY1_PLL0_PDIAG_CTRL 0x80680 +#define PX210_PHY1_PLL0_VCOCAL_PLLCNT_START 0x80220 +#define PX210_PHY1_PLL0_LOCK_PEFCNT 0x80270 +#define PX210_PHY1_PLL0_LOCK_PLLCNT_START 0x80278 +#define PX210_PHY1_PLL0_LOCK_PLLCNT_THR 0x8027c + +#define PX210_PHY0_PLL0_TX_PSC_A0 0x18400 +#define PX210_PHY1_PLL0_TX_PSC_A0 0x90400 + #define PLL0_TX_PSC_A0 0xfb +#define PX210_PHY0_PLL0_TX_PSC_A2 0x18408 +#define PX210_PHY1_PLL0_TX_PSC_A2 0x90408 + #define PLL0_TX_PSC_A2 0x4aa +#define PX210_PHY0_PLL0_TX_PSC_A3 0x1840c +#define PX210_PHY1_PLL0_TX_PSC_A3 0x9040c + #define PLL0_TX_PSC_A3 0x4aa +#define PX210_PHY0_PLL0_RX_PSC_A0 0x28000 +#define PX210_PHY1_PLL0_RX_PSC_A0 0xa0000 + #define PLL0_RX_PSC_A0 0x0 +#define PX210_PHY0_PLL0_RX_PSC_A2 0x28008 +#define PX210_PHY1_PLL0_RX_PSC_A2 0xa0008 + #define PLL0_RX_PSC_A2 0x0 +#define PX210_PHY0_PLL0_RX_PSC_A3 0x2800C +#define PX210_PHY1_PLL0_RX_PSC_A3 0xa000C + #define PLL0_RX_PSC_A3 0x0 +#define PX210_PHY0_PLL0_RX_PSC_CAL 0x28018 +#define PX210_PHY1_PLL0_RX_PSC_CAL 0xa0018 + #define PLL0_RX_PSC_CAL 0x0 + +#define PX210_PHY0_PLL1_TX_PSC_A0 0x1a400 + #define PLL1_TX_PSC_A0 0xfb +#define PX210_PHY0_PLL1_TX_PSC_A2 0x1a408 + #define PLL1_TX_PSC_A2 0x4aa +#define PX210_PHY0_PLL1_TX_PSC_A3 0x1a40c + #define PLL1_TX_PSC_A3 0x4aa +#define PX210_PHY0_PLL1_RX_PSC_A0 0x2a000 + #define PLL1_RX_PSC_A0 0x0 +#define PX210_PHY0_PLL1_RX_PSC_A2 0x2a008 + #define PLL1_RX_PSC_A2 0x0 +#define PX210_PHY0_PLL1_RX_PSC_A3 0x2a00C + #define PLL1_RX_PSC_A3 0x0 +#define PX210_PHY0_PLL1_RX_PSC_CAL 0x2a018 + #define PLL1_RX_PSC_CAL 0x0 + +#define PX210_PHY0_PLL0_XCVR_CTRL 0x183a8 +#define PX210_PHY1_PLL0_XCVR_CTRL 0x903a8 + #define PLL0_XCVR_CTRL 0xf +#define PX210_PHY0_PLL1_XCVR_CTRL 0x1a3a8 + #define PLL1_XCVR_CTRL 0xf + +#define PX210_PHY0_PLL0_RX_GCSM1_CTRL 0x28420 +#define PX210_PHY1_PLL0_RX_GCSM1_CTRL 0xa0420 + #define PLL0_RX_GCSM1_CTRL 0x0 +#define PX210_PHY0_PLL0_RX_GCSM2_CTRL 0x28440 +#define PX210_PHY1_PLL0_RX_GCSM2_CTRL 0xa0440 + #define PLL0_RX_GCSM2_CTRL 0x0 +#define PX210_PHY0_PLL0_RX_PERGCSM_CTRL 0x28460 +#define PX210_PHY1_PLL0_RX_PERGCSM_CTRL 0xa0460 + #define PLL0_RX_PERGCSM_CTRL 0x0 + +#define PX210_PHY0_PLL1_RX_GCSM1_CTRL 0x2a420 + #define PLL1_RX_GCSM1_CTRL 0x0 +#define PX210_PHY0_PLL1_RX_GCSM2_CTRL 0x2a440 + #define PLL1_RX_GCSM2_CTRL 0x0 +#define PX210_PHY0_PLL1_RX_PERGCSM_CTRL 0x2a460 + #define PLL1_RX_PERGCSM_CTRL 0x0 + +/* swing and emphasis */ +#define PX210_PHY0_PLL0_TX_DIAG_ACYA 0x1879c +#define PX210_PHY0_PLL1_TX_DIAG_ACYA 0x1a79c +#define PX210_PHY1_PLL0_TX_DIAG_ACYA 0x9079c + #define LOCK 1 + #define UNLOCK 0 + +#define PX210_PHY0_PLL0_TX_TXCC_CTRL 0x18100 +#define PX210_PHY0_PLL1_TX_TXCC_CTRL 0x1a100 +#define PX210_PHY1_PLL0_TX_TXCC_CTRL 0x90100 + #define TX_TXCC_CTRL 0x8a4 + +#define PX210_PHY0_PLL0_TX_DRV 0x18318 +#define PX210_PHY0_PLL1_TX_DRV 0x1a318 +#define PX210_PHY1_PLL0_TX_DRV 0x90318 + #define TX_DRV 0x3 + +#define PX210_PHY0_PLL0_TX_MGNFS 0x18140 +#define PX210_PHY0_PLL1_TX_MGNFS 0x1a140 +#define PX210_PHY1_PLL0_TX_MGNFS 0x90140 + +#define PX210_PHY0_PLL0_TX_CPOST 0x18130 +#define PX210_PHY0_PLL1_TX_CPOST 0x1a130 +#define PX210_PHY0_PLL1_TX_CPOST1 0x1a13c +#define PX210_PHY1_PLL0_TX_CPOST 0x90130 + +/******************************phy register end********************************************/ +#endif /* __PX210_REG_H__ */ diff --git a/target/linux/phytium/files-5.10/drivers/hwmon/tacho-phytium.c b/target/linux/phytium/files-5.10/drivers/hwmon/tacho-phytium.c new file mode 100644 index 000000000..cbfbe0b82 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/hwmon/tacho-phytium.c @@ -0,0 +1,391 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Hwmon driver for Phytium tachometer. + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TIMER_CTRL_REG 0x00 +#define TIMER_CTRL_MODE_SHIFT 0//0:1 +#define TIMER_CTRL_RESET_SHIFT BIT(2) +#define TIMER_CTRL_FORCE_SHIFT BIT(3) +#define TIMER_CTRL_CAPTURE_EN_SHIFT BIT(4) +#define TIMER_CTRL_CAPTURE_CNT_SHIFT 5//5:11 +#define TIMER_CTRL_ANTI_JITTER_SHIFT 18//18:19 +#define TIMER_CTRL_TACHO_MODE_SHIFT 20//20:21 +#define TIMER_CTRL_TIMER_CNT_MODE_SHIFT BIT(22) +#define TIMER_CTRL_BIT_SET_SHIFT 24 +#define TIMER_CTRL_CNT_EN_SHIFT BIT(25) +#define TIMER_CTRL_CNT_CLR_SHIFT BIT(26) +#define TIMER_CTRL_TIMER_MODE_SHIFT BIT(27) +#define TIMER_CTRL_PULSE_NUM_SHIFT 28//28:30 +#define TIMER_CTRL_TACHO_EN_SHIFT BIT(31) +#define TIMER_TACHO_RES_REG 0x04 +#define TIMER_TACHO_RES_VALID_SHIFT BIT(31) +#define TIMER_TACHO_RES_MASK GENMASK(30, 0) +#define TIMER_CMP_VALUE_UP_REG 0x08 +#define TIMER_CMP_VALUE_LOW_REG 0x1C +#define TIMER_CNT_VALUE_UP_REG 0x20 +#define TIMER_CNT_VALUE_LOW_REG 0x24 +#define TIMER_INT_MASK_REG 0x28 +#define TIMER_INT_STAT_REG 0x2C +#define TIMER_INT_CAPTURE_SHIFT BIT(5) +#define TIMER_INT_CYC_COMP_SHIFT BIT(4) +#define TIMER_INT_ONE_COMP_SHIFT BIT(3) +#define TIMER_INT_ROLLOVER_SHIFT BIT(2) +#define TIMER_INT_TACHO_UNDER_SHIFT BIT(1) +#define TIMER_INT_TACHO_OVER_SHIFT BIT(0) +#define TIMER_TACHO_OVER_REG 0x30 +#define TIMER_TACHO_UNDER_REG 0x34 +#define TIMER_START_VALUE_REG 0x38 + +#define TIMER_INT_CLR_MASK GENMASK(5, 0) + +enum tacho_modes { +tacho_mode = 1, +capture_mode, +}; + +enum edge_modes { +rising_edge, +falling_edge, +double_edge, +}; + +struct phytium_tacho { + struct device *dev; + struct device *hwmon; + void __iomem *base; + struct clk *clk; + u32 freq; + int irq; + u8 work_mode; + u8 edge_mode; + u32 debounce; +}; + +static u16 capture_count; + +static void phytium_tacho_init(struct phytium_tacho *tacho) +{ + u32 val; + + if (tacho->work_mode == tacho_mode) { + val = (TIMER_CTRL_TACHO_EN_SHIFT | + TIMER_CTRL_CNT_EN_SHIFT | + (tacho->edge_mode << TIMER_CTRL_TACHO_MODE_SHIFT) | + (tacho->debounce << TIMER_CTRL_ANTI_JITTER_SHIFT) | + (tacho->work_mode << TIMER_CTRL_MODE_SHIFT)); + writel_relaxed(val, tacho->base + TIMER_CTRL_REG); + writel_relaxed(0x2faf07f, tacho->base + TIMER_CMP_VALUE_LOW_REG); + } else { + val = (TIMER_CTRL_TACHO_EN_SHIFT | + TIMER_CTRL_CNT_EN_SHIFT | + (tacho->edge_mode << TIMER_CTRL_TACHO_MODE_SHIFT) | + (tacho->debounce << TIMER_CTRL_ANTI_JITTER_SHIFT) | + TIMER_CTRL_CAPTURE_EN_SHIFT | + (0x7f << TIMER_CTRL_CAPTURE_CNT_SHIFT) | + (tacho->work_mode << TIMER_CTRL_MODE_SHIFT)), + writel_relaxed(val, tacho->base + TIMER_CTRL_REG); + writel_relaxed(0x20, tacho->base + TIMER_INT_MASK_REG); + } +} + +static int phytium_get_fan_tach_rpm(struct phytium_tacho *priv) +{ + u64 raw_data, tach_div, clk_source; + u8 mode, both; + unsigned long timeout; + unsigned long loopcounter; + + timeout = jiffies + msecs_to_jiffies(500); + + for (loopcounter = 0;; loopcounter++) { + raw_data = readl_relaxed(priv->base + TIMER_TACHO_RES_REG); + + if (raw_data & TIMER_TACHO_RES_VALID_SHIFT) + break; + + if (time_after(jiffies, timeout)) + return -ETIMEDOUT; + + if (loopcounter > 3000) + msleep(20); + else { + udelay(100); + cond_resched(); + } + } + + raw_data = raw_data & TIMER_TACHO_RES_MASK; + clk_source = priv->freq; + mode = priv->edge_mode; + both = (mode == double_edge) ? 1 : 0; + tach_div = 1 << both; + + if (raw_data == 0) + return 0; + + return (clk_source * 60 * raw_data) / 0x2faf080 / tach_div; +} + +static ssize_t show_rpm(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int rpm; + struct phytium_tacho *priv = dev_get_drvdata(dev); + + rpm = phytium_get_fan_tach_rpm(priv); + if (rpm < 0) + return rpm; + + return sprintf(buf, "%d\n", rpm); +} + +static SENSOR_DEVICE_ATTR(fan_input, 0444, + show_rpm, NULL, 0); + +static struct attribute *tacho_dev_attrs[] = { + &sensor_dev_attr_fan_input.dev_attr.attr, + NULL +}; + +static umode_t tacho_dev_is_visible(struct kobject *kobj, + struct attribute *a, int index) +{ + return a->mode; +} + +static const struct attribute_group tacho_group = { + .attrs = tacho_dev_attrs, + .is_visible = tacho_dev_is_visible, +}; + +static const struct attribute_group *tacho_groups[] = { + &tacho_group, + NULL +}; + +static irqreturn_t capture_irq_handler(int irq, void *dev_id) +{ + struct phytium_tacho *priv = dev_id; + u32 status = readl_relaxed(priv->base + TIMER_INT_STAT_REG); + + if (status & TIMER_INT_CAPTURE_SHIFT) { + capture_count++; + + if (capture_count == 0) + dev_err(priv->dev, "Capture counter is overflowed"); + + writel_relaxed(status, priv->base + TIMER_INT_STAT_REG); + return IRQ_HANDLED; + } + return IRQ_NONE; +} + +static ssize_t show_capture(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int cnt; + struct phytium_tacho *priv = dev_get_drvdata(dev); + + cnt = capture_count * 0x7f + readl_relaxed(priv->base + TIMER_CNT_VALUE_LOW_REG); + + return sprintf(buf, "%d\n", cnt); +} + +static SENSOR_DEVICE_ATTR(capture_input, 0444, + show_capture, NULL, 0); + +static struct attribute *capture_dev_attrs[] = { + &sensor_dev_attr_capture_input.dev_attr.attr, + NULL +}; + +static umode_t capture_dev_is_visible(struct kobject *kobj, + struct attribute *a, int index) +{ + return a->mode; +} + +static const struct attribute_group capture_group = { + .attrs = capture_dev_attrs, + .is_visible = capture_dev_is_visible, +}; + +static const struct attribute_group *capture_groups[] = { + &capture_group, + NULL +}; + +static int phytium_tacho_get_work_mode(struct phytium_tacho *tacho) +{ + struct fwnode_handle *nc = dev_fwnode(tacho->dev); + + if (fwnode_property_read_bool(nc, "tacho")) + return tacho_mode; + if (fwnode_property_read_bool(nc, "capture")) + return capture_mode; + return tacho_mode; +} + +static int phytium_tacho_get_edge_mode(struct phytium_tacho *tacho) +{ + struct fwnode_handle *nc = dev_fwnode(tacho->dev); + + if (fwnode_property_read_bool(nc, "up")) + return rising_edge; + if (fwnode_property_read_bool(nc, "down")) + return falling_edge; + if (fwnode_property_read_bool(nc, "double")) + return double_edge; + return rising_edge; +} + +static int phytium_tacho_get_debounce(struct phytium_tacho *tacho) +{ + u32 value; + struct fwnode_handle *nc = dev_fwnode(tacho->dev); + + if (!fwnode_property_read_u32(nc, "debounce-level", &value)) + return value; + else + return 0; +} + +static void phytium_tacho_get_of_data(struct phytium_tacho *tacho) +{ + tacho->work_mode = phytium_tacho_get_work_mode(tacho); + tacho->edge_mode = phytium_tacho_get_edge_mode(tacho); + tacho->debounce = phytium_tacho_get_debounce(tacho); +} + +static int phytium_tacho_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *res; + struct phytium_tacho *tacho; + int ret; + + tacho = devm_kzalloc(dev, sizeof(*tacho), GFP_KERNEL); + if (!tacho) + return -ENOMEM; + + tacho->dev = &pdev->dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENOENT; + + tacho->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(tacho->base)) { + dev_err(&pdev->dev, "region map failed\n"); + return PTR_ERR(tacho->base); + } + if (dev->of_node) { + tacho->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(tacho->clk)) + return PTR_ERR(tacho->clk); + ret = clk_prepare_enable(tacho->clk); + if (ret) + return ret; + + tacho->freq = clk_get_rate(tacho->clk); + } else if (has_acpi_companion(dev)){ + if(fwnode_property_read_u32(dev_fwnode(dev),"clock-frequency", (u32 *)&(tacho->freq) ) <0) + tacho->freq = 50000000; + } + + tacho->irq = platform_get_irq(pdev, 0); + if (tacho->irq < 0) { + dev_err(&pdev->dev, "no irq resource?\n"); + return tacho->irq; + } + + ret = devm_request_irq(dev, tacho->irq, capture_irq_handler, + 0, "phytium_tacho", tacho); + if (ret) { + dev_err(&pdev->dev, "Cannot request IRQ\n"); + return ret; + } + + phytium_tacho_get_of_data(tacho); + + phytium_tacho_init(tacho); + + if (tacho->work_mode == tacho_mode) + tacho->hwmon = devm_hwmon_device_register_with_groups(dev, + "phytium_tacho", + tacho, tacho_groups); + else + tacho->hwmon = devm_hwmon_device_register_with_groups(dev, + "phytium_capture", + tacho, capture_groups); + + platform_set_drvdata(pdev, tacho); + + return PTR_ERR_OR_ZERO(tacho->hwmon); +} + +#ifdef CONFIG_PM_SLEEP +static int phytium_tacho_suspend(struct device *dev) +{ + return 0; +} + +static int phytium_tacho_resume(struct device *dev) +{ + struct phytium_tacho *tacho = dev_get_drvdata(dev); + + phytium_tacho_init(tacho); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(phytium_tacho_pm, phytium_tacho_suspend, phytium_tacho_resume); + +#ifdef CONFIG_ACPI +static const struct acpi_device_id phytium_tacho_acpi_ids[] = { + { "PHYT0033", 0 }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(acpi, phytium_tacho_acpi_ids); +#endif + +static const struct of_device_id tacho_of_match[] = { + { .compatible = "phytium,tacho", }, + {}, +}; +MODULE_DEVICE_TABLE(of, tacho_of_match); + +static struct platform_driver phytium_tacho_driver = { + .probe = phytium_tacho_probe, + .driver = { + .name = "phytium_tacho", + .pm = &phytium_tacho_pm, + .of_match_table = of_match_ptr(tacho_of_match), + .acpi_match_table = ACPI_PTR(phytium_tacho_acpi_ids), + }, +}; + +module_platform_driver(phytium_tacho_driver); + +MODULE_AUTHOR("Zhang Yiqun "); +MODULE_DESCRIPTION("Phytium tachometer driver"); +MODULE_LICENSE("GPL"); diff --git a/target/linux/phytium/files-5.10/drivers/hwspinlock/phytium_hwspinlock.c b/target/linux/phytium/files-5.10/drivers/hwspinlock/phytium_hwspinlock.c new file mode 100644 index 000000000..cea34c51a --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/hwspinlock/phytium_hwspinlock.c @@ -0,0 +1,223 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Phytium hardware spinlock driver + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hwspinlock_internal.h" + +/* Spinlock register offsets */ +#define LOCK_BASE 0x10 + +#define SEMA_NOTTAKEN (0) /* free */ +#define SEMA_TAKEN (1) /* locked */ + +static int phytium_hwspinlock_trylock(struct hwspinlock *lock) +{ + void __iomem *lock_addr = lock->priv; + + /* attempt to acquire the lock by reading its value */ + return (readl(lock_addr) == SEMA_NOTTAKEN); +} + +static void phytium_hwspinlock_unlock(struct hwspinlock *lock) +{ + void __iomem *lock_addr = lock->priv; + + /* release the lock by writing 0 to it */ + writel(SEMA_NOTTAKEN, lock_addr); +} + +static void phytium_hwspinlock_relax(struct hwspinlock *lock) +{ + ndelay(50); +} + +static const struct hwspinlock_ops phytium_hwspinlock_ops = { + .trylock = phytium_hwspinlock_trylock, + .unlock = phytium_hwspinlock_unlock, + .relax = phytium_hwspinlock_relax, +}; + +static ssize_t phytium_hwlock_test_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct hwspinlock *hwlock = NULL; + int ret; + + /* dynamically assign a hwspinlock */ + hwlock = hwspin_lock_request(); + if (!hwlock) { + pr_err("%s can't request hwlock\n", __func__); + return -ENODEV; + } + ret = hwspin_lock_timeout(hwlock, 1000); + if (ret < 0) { + pr_err("do lock fail\n"); + goto err; + } + pr_info("do lock success\n"); + + ret = hwspin_trylock(hwlock); + if (ret == -EINVAL) { + pr_err("lock invalid\n"); + goto err; + } + else + pr_info("lock status is %s before, than unlock it\n", ret ? "locked" : "no use"); + + pr_info("do lock test pass\n"); +err: + hwspin_unlock(hwlock); + hwspin_lock_free(hwlock); + return count; +} + +static DEVICE_ATTR_WO(phytium_hwlock_test); + + +static int phytium_hwspinlock_probe(struct platform_device *pdev) +{ + struct fwnode_handle *np = dev_fwnode(&(pdev->dev)); + struct hwspinlock_device *bank; + struct hwspinlock *hwlock; + struct resource *res; + void __iomem *io_base; + int num_locks, i, ret; + + if (!np) + return -ENODEV; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + io_base = ioremap(res->start, resource_size(res)); + if (!io_base) + return -ENOMEM; + + /* + * make sure the module is enabled and clocked before reading + * the module SYSSTATUS register + */ + pm_runtime_enable(&pdev->dev); + ret = pm_runtime_get_sync(&pdev->dev); + if (ret < 0) { + pm_runtime_put_noidle(&pdev->dev); + goto iounmap_base; + } + + /* Determine number of locks */ + if (fwnode_property_read_u32(np, "nr-locks", &num_locks)) { + dev_err(&pdev->dev, "missing/invalid number of locks\n"); + ret = -EINVAL; + goto iounmap_base; + } + + /* + * runtime PM will make sure the clock of this module is + * enabled again iff at least one lock is requested + */ + ret = pm_runtime_put(&pdev->dev); + if (ret < 0) + goto iounmap_base; + + bank = kzalloc(struct_size(bank, lock, num_locks), GFP_KERNEL); + if (!bank) { + ret = -ENOMEM; + goto iounmap_base; + } + + platform_set_drvdata(pdev, bank); + + for (i = 0, hwlock = &bank->lock[0]; i < num_locks; i++, hwlock++) { + /* Set register address of each lock */ + hwlock->priv = io_base + LOCK_BASE + sizeof(u32) * i; + } + + ret = hwspin_lock_register(bank, &pdev->dev, &phytium_hwspinlock_ops, + 0, num_locks); + if (ret) + goto reg_fail; + + ret = device_create_file(&pdev->dev, &dev_attr_phytium_hwlock_test); + return ret; +reg_fail: + kfree(bank); +iounmap_base: + iounmap(io_base); + return ret; +} + +static int phytium_hwspinlock_remove(struct platform_device *pdev) +{ + struct hwspinlock_device *bank = platform_get_drvdata(pdev); + void __iomem *io_base = bank->lock[0].priv - LOCK_BASE; + int ret; + + ret = hwspin_lock_unregister(bank); + if (ret) { + dev_err(&pdev->dev, "%s failed: %d\n", __func__, ret); + return ret; + } + + iounmap(io_base); + kfree(bank); + + return 0; +} + +#ifdef CONFIG_ACPI +static const struct acpi_device_id phytium_hwspinlock_acpi_ids[] = { + { "PHYT0027", 0 }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(acpi, phytium_hwspinlock_acpi_ids); +#endif + +static const struct of_device_id phytium_hwspinlock_of_match[] = { + { .compatible = "phytium,hwspinlock", }, + { /* end */ }, +}; +MODULE_DEVICE_TABLE(of, phytium_hwspinlock_of_match); + +static struct platform_driver phytium_hwspinlock_driver = { + .probe = phytium_hwspinlock_probe, + .remove = phytium_hwspinlock_remove, + .driver = { + .name = "phytium_hwspinlock", + .of_match_table = of_match_ptr(phytium_hwspinlock_of_match), + .acpi_match_table = ACPI_PTR(phytium_hwspinlock_acpi_ids), + }, +}; + +static int __init phytium_hwspinlock_init(void) +{ + return platform_driver_register(&phytium_hwspinlock_driver); +} +postcore_initcall(phytium_hwspinlock_init); + +static void __exit phytium_hwspinlock_exit(void) +{ + platform_driver_unregister(&phytium_hwspinlock_driver); +} +module_exit(phytium_hwspinlock_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Hardware spinlock driver for Phytium"); +MODULE_AUTHOR("Chen Baozi "); diff --git a/target/linux/phytium/files-5.10/drivers/i2c/busses/i2c-phytium-common.c b/target/linux/phytium/files-5.10/drivers/i2c/busses/i2c-phytium-common.c new file mode 100644 index 000000000..5c3ee3758 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/i2c/busses/i2c-phytium-common.c @@ -0,0 +1,203 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Phytium I2C adapter driver. + * + * Based on the TI DAVINCI I2C adapter driver. + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "i2c-phytium-core.h" + +static char *abort_sources[] = { + [ABRT_7B_ADDR_NOACK] = + "slave address not acknowledged (7bit mode)", + [ABRT_10ADDR1_NOACK] = + "first address byte not acknowledged (10bit mode)", + [ABRT_10ADDR2_NOACK] = + "second address byte not acknowledged (10bit mode)", + [ABRT_TXDATA_NOACK] = + "data not acknowledged", + [ABRT_GCALL_NOACK] = + "no acknowledgment for a general call", + [ABRT_GCALL_READ] = + "read after general call", + [ABRT_SBYTE_ACKDET] = + "start byte acknowledged", + [ABRT_SBYTE_NORSTRT] = + "trying to send start byte when restart is disabled", + [ABRT_10B_RD_NORSTRT] = + "trying to read when restart is disabled (10bit mode)", + [ABRT_MASTER_DIS] = + "trying to use disabled adapter", + [ARB_LOST] = + "lost arbitration", + [ABRT_SLAVE_FLUSH_TXFIFO] = + "read command so flush old data in the TX FIFO", + [ABRT_SLAVE_ARBLOST] = + "slave lost the bus while transmitting data to a remote master", + [ABRT_SLAVE_RD_INTX] = + "incorrect slave-transmitter mode configuration", +}; + +u32 phytium_readl(struct phytium_i2c_dev *dev, int offset) +{ + return readl_relaxed(dev->base + offset); +} + +void phytium_writel(struct phytium_i2c_dev *dev, u32 b, int offset) +{ + writel_relaxed(b, dev->base + offset); +} + +u32 i2c_phytium_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset) +{ + if (cond) + return (ic_clk * tSYMBOL + 500000) / 1000000 - 8 + offset; + else + return (ic_clk * (tSYMBOL + tf) + 500000) / 1000000 - 3 + offset; +} + +u32 i2c_phytium_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset) +{ + return ((ic_clk * (tLOW + tf) + 500000) / 1000000) - 1 + offset; +} + +int i2c_phytium_set_sda_hold(struct phytium_i2c_dev *dev) +{ + if (!dev->sda_hold_time) { + /* Keep previous hold time setting if no one set it */ + dev->sda_hold_time = phytium_readl(dev, IC_SDA_HOLD); + } + + if (!(dev->sda_hold_time & IC_SDA_HOLD_RX_MASK)) + dev->sda_hold_time |= 1 << IC_SDA_HOLD_RX_SHIFT; + + dev_dbg(dev->dev, "SDA Hold Time TX:RX = %d:%d\n", + dev->sda_hold_time & ~(u32)IC_SDA_HOLD_RX_MASK, + dev->sda_hold_time >> IC_SDA_HOLD_RX_SHIFT); + + return 0; +} + +void __i2c_phytium_disable(struct phytium_i2c_dev *dev) +{ + int timeout = 100; + + do { + __i2c_phytium_disable_nowait(dev); + if ((phytium_readl(dev, IC_ENABLE_STATUS) & 1) == 0) + return; + + /* + * Wait 10 times the signaling period of the highest I2C + * transfer supported by the driver (for 400KHz this is + * 25us). + */ + usleep_range(25, 250); + } while (timeout--); + + dev_warn(dev->dev, "timeout in disabling adapter\n"); +} + +unsigned long i2c_phytium_clk_rate(struct phytium_i2c_dev *dev) +{ + if (WARN_ON_ONCE(!dev->get_clk_rate_khz)) + return 0; + return dev->get_clk_rate_khz(dev); +} + +int i2c_phytium_prepare_clk(struct phytium_i2c_dev *dev, bool prepare) +{ + if (IS_ERR(dev->clk)) + return PTR_ERR(dev->clk); + + if (prepare) + return clk_prepare_enable(dev->clk); + + clk_disable_unprepare(dev->clk); + return 0; +} +EXPORT_SYMBOL_GPL(i2c_phytium_prepare_clk); + +int i2c_phytium_wait_bus_not_busy(struct phytium_i2c_dev *dev) +{ + int timeout = 20; /* 20 ms */ + + while (phytium_readl(dev, IC_STATUS) & IC_STATUS_ACTIVITY) { + if (timeout <= 0) { + dev_warn(dev->dev, "timeout waiting for bus ready\n"); + i2c_recover_bus(&dev->adapter); + + if (phytium_readl(dev, IC_STATUS) & IC_STATUS_ACTIVITY) + return -ETIMEDOUT; + return 0; + } + timeout--; + usleep_range(1000, 1100); + } + + return 0; +} + +int i2c_phytium_handle_tx_abort(struct phytium_i2c_dev *dev) +{ + unsigned long abort_source = dev->abort_source; + int i; + + if (abort_source & IC_TX_ABRT_NOACK) { + for_each_set_bit(i, &abort_source, ARRAY_SIZE(abort_sources)) + dev_dbg(dev->dev, + "%s: %s\n", __func__, abort_sources[i]); + return -EREMOTEIO; + } + + for_each_set_bit(i, &abort_source, ARRAY_SIZE(abort_sources)) + dev_err(dev->dev, "%s: %s\n", __func__, abort_sources[i]); + + if (abort_source & IC_TX_ARB_LOST) + return -EAGAIN; + else if (abort_source & IC_TX_ABRT_GCALL_READ) + return -EINVAL; + else + return -EIO; + + return 0; +} + +u32 i2c_phytium_func(struct i2c_adapter *adapter) +{ + struct phytium_i2c_dev *dev = i2c_get_adapdata(adapter); + + return dev->functionality; +} + +void i2c_phytium_disable(struct phytium_i2c_dev *dev) +{ + /* Disable controller */ + __i2c_phytium_disable(dev); + + /* Disable all interrupts */ + phytium_writel(dev, 0, IC_INTR_MASK); + phytium_readl(dev, IC_CLR_INTR); +} + +void i2c_phytium_disable_int(struct phytium_i2c_dev *dev) +{ + phytium_writel(dev, 0, IC_INTR_MASK); +} + +MODULE_AUTHOR("Cheng Quan "); +MODULE_DESCRIPTION("Phytium I2C bus adapter core"); +MODULE_LICENSE("GPL"); diff --git a/target/linux/phytium/files-5.10/drivers/i2c/busses/i2c-phytium-core.h b/target/linux/phytium/files-5.10/drivers/i2c/busses/i2c-phytium-core.h new file mode 100644 index 000000000..de07ae718 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/i2c/busses/i2c-phytium-core.h @@ -0,0 +1,254 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Phytium I2C adapter driver. + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#include +#include +#include + +#define 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 IC_CON_MASTER 0x1 +#define IC_CON_SPEED_STD 0x2 +#define IC_CON_SPEED_FAST 0x4 +#define IC_CON_SPEED_HIGH 0x6 +#define IC_CON_SPEED_MASK 0x6 +#define IC_CON_10BITADDR_SLAVE 0x8 +#define IC_CON_10BITADDR_MASTER 0x10 +#define IC_CON_RESTART_EN 0x20 +#define IC_CON_SLAVE_DISABLE 0x40 +#define IC_CON_STOP_DET_IFADDRESSED 0x80 +#define IC_CON_TX_EMPTY_CTRL 0x100 +#define IC_CON_RX_FIFO_FULL_HLD_CTRL 0x200 + +#define IC_CON 0x0 +#define IC_TAR 0x4 +#define IC_SAR 0x8 +#define IC_DATA_CMD 0x10 +#define IC_SS_SCL_HCNT 0x14 +#define IC_SS_SCL_LCNT 0x18 +#define IC_FS_SCL_HCNT 0x1c +#define IC_FS_SCL_LCNT 0x20 +#define IC_HS_SCL_HCNT 0x24 +#define IC_HS_SCL_LCNT 0x28 +#define IC_INTR_STAT 0x2c +#define IC_INTR_MASK 0x30 +#define IC_RAW_INTR_STAT 0x34 +#define IC_RX_TL 0x38 +#define IC_TX_TL 0x3c +#define IC_CLR_INTR 0x40 +#define IC_CLR_RX_UNDER 0x44 +#define IC_CLR_RX_OVER 0x48 +#define IC_CLR_TX_OVER 0x4c +#define IC_CLR_RD_REQ 0x50 +#define IC_CLR_TX_ABRT 0x54 +#define IC_CLR_RX_DONE 0x58 +#define IC_CLR_ACTIVITY 0x5c +#define IC_CLR_STOP_DET 0x60 +#define IC_CLR_START_DET 0x64 +#define IC_CLR_GEN_CALL 0x68 +#define IC_ENABLE 0x6c +#define IC_STATUS 0x70 +#define IC_TXFLR 0x74 +#define IC_RXFLR 0x78 +#define IC_SDA_HOLD 0x7c +#define IC_TX_ABRT_SOURCE 0x80 +#define IC_ENABLE_STATUS 0x9c +#define IC_SMBCLK_LOW_MEXT 0xa8 +#define IC_SMBCLK_LOW_TIMEOUT 0xac +#define IC_SMBDAT_STUCK_TIMEOUT 0xb4 +#define IC_CLR_SMBCLK_EXT_LOW_TIMEOUT 0xbc +#define IC_CLR_SMBCLK_TMO_LOW_TIMEOUT 0xc0 +#define IC_CLR_SMBDAT_LOW_TIMEOUT 0xc4 +#define IC_CLR_SMBALERT_IN_N 0xd0 + +#define IC_INTR_RX_UNDER 0x001 +#define IC_INTR_RX_OVER 0x002 +#define IC_INTR_RX_FULL 0x004 +#define IC_INTR_TX_OVER 0x008 +#define IC_INTR_TX_EMPTY 0x010 +#define IC_INTR_RD_REQ 0x020 +#define IC_INTR_TX_ABRT 0x040 +#define IC_INTR_RX_DONE 0x080 +#define IC_INTR_ACTIVITY 0x100 +#define IC_INTR_STOP_DET 0x200 +#define IC_INTR_START_DET 0x400 +#define IC_INTR_GEN_CALL 0x800 +#define IC_INTR_SMBCLK_EXT_LOW_TIMEOUT 0x1000 +#define IC_INTR_SMBCLK_TMO_LOW_TIMEOUT 0x2000 +#define IC_INTR_SMBSDA_LOW_TIMEOUT 0x4000 +#define IC_INTR_SMBALERT_IN_N 0x20000 + +#define IC_INTR_DEFAULT_MASK (IC_INTR_RX_FULL | \ + IC_INTR_TX_ABRT | \ + IC_INTR_STOP_DET) +#define IC_INTR_MASTER_MASK (IC_INTR_DEFAULT_MASK | \ + IC_INTR_TX_EMPTY) +#define IC_INTR_SLAVE_MASK (IC_INTR_DEFAULT_MASK | \ + IC_INTR_RX_DONE | \ + IC_INTR_RX_UNDER | \ + IC_INTR_RD_REQ) +#define IC_INTR_SMBUS_MASK (IC_INTR_MASTER_MASK | \ + IC_INTR_SMBCLK_EXT_LOW_TIMEOUT | \ + IC_INTR_SMBCLK_TMO_LOW_TIMEOUT | \ + IC_INTR_SMBSDA_LOW_TIMEOUT) + +#define IC_STATUS_ACTIVITY 0x1 +#define IC_STATUS_TFE BIT(2) +#define IC_STATUS_MASTER_ACTIVITY BIT(5) +#define IC_STATUS_SLAVE_ACTIVITY BIT(6) + +#define IC_SDA_HOLD_RX_SHIFT 16 +#define IC_SDA_HOLD_RX_MASK GENMASK(23, IC_SDA_HOLD_RX_SHIFT) + +#define IC_ERR_TX_ABRT 0x1 + +#define IC_TAR_10BITADDR_MASTER BIT(12) + +#define IC_COMP_PARAM_1_SPEED_MODE_HIGH (BIT(2) | BIT(3)) +#define IC_COMP_PARAM_1_SPEED_MODE_MASK GENMASK(3, 2) + +#define STATUS_IDLE 0x0 +#define STATUS_WRITE_IN_PROGRESS 0x1 +#define STATUS_READ_IN_PROGRESS 0x2 + +/* + * operation modes + */ +#define PHYTIUM_IC_MASTER 0 +#define PHYTIUM_IC_SLAVE 1 + +#define ABRT_7B_ADDR_NOACK 0 +#define ABRT_10ADDR1_NOACK 1 +#define ABRT_10ADDR2_NOACK 2 +#define ABRT_TXDATA_NOACK 3 +#define ABRT_GCALL_NOACK 4 +#define ABRT_GCALL_READ 5 +#define ABRT_SBYTE_ACKDET 7 +#define ABRT_SBYTE_NORSTRT 9 +#define ABRT_10B_RD_NORSTRT 10 +#define ABRT_MASTER_DIS 11 +#define ARB_LOST 12 +#define ABRT_SLAVE_FLUSH_TXFIFO 13 +#define ABRT_SLAVE_ARBLOST 14 +#define ABRT_SLAVE_RD_INTX 15 + +#define IC_TX_ABRT_7B_ADDR_NOACK (1UL << ABRT_7B_ADDR_NOACK) +#define IC_TX_ABRT_10ADDR1_NOACK (1UL << ABRT_10ADDR1_NOACK) +#define IC_TX_ABRT_10ADDR2_NOACK (1UL << ABRT_10ADDR2_NOACK) +#define IC_TX_ABRT_TXDATA_NOACK (1UL << ABRT_TXDATA_NOACK) +#define IC_TX_ABRT_GCALL_NOACK (1UL << ABRT_GCALL_NOACK) +#define IC_TX_ABRT_GCALL_READ (1UL << ABRT_GCALL_READ) +#define IC_TX_ABRT_SBYTE_ACKDET (1UL << ABRT_SBYTE_ACKDET) +#define IC_TX_ABRT_SBYTE_NORSTRT (1UL << ABRT_SBYTE_NORSTRT) +#define IC_TX_ABRT_10B_RD_NORSTRT (1UL << ABRT_10B_RD_NORSTRT) +#define IC_TX_ABRT_MASTER_DIS (1UL << ABRT_MASTER_DIS) +#define IC_TX_ARB_LOST (1UL << ARB_LOST) +#define IC_RX_ABRT_SLAVE_RD_INTX (1UL << ABRT_SLAVE_RD_INTX) +#define IC_RX_ABRT_SLAVE_ARBLOST (1UL << ABRT_SLAVE_ARBLOST) +#define IC_RX_ABRT_SLAVE_FLUSH_TXFIFO (1UL << ABRT_SLAVE_FLUSH_TXFIFO) + +#define IC_TX_ABRT_NOACK (IC_TX_ABRT_7B_ADDR_NOACK | \ + IC_TX_ABRT_10ADDR1_NOACK | \ + IC_TX_ABRT_10ADDR2_NOACK | \ + IC_TX_ABRT_TXDATA_NOACK | \ + IC_TX_ABRT_GCALL_NOACK) +#define CONTROLLER_TYPE_IIC 0 +#define CONTROLLER_TYPE_SMBUS 1 + +struct phytium_i2c_dev { + struct device *dev; + void __iomem *base; + int irq; + u32 flags; + struct completion cmd_complete; + struct clk *clk; + struct reset_control *rst; + int mode; + struct i2c_client *slave; + u32 (*get_clk_rate_khz)(struct phytium_i2c_dev *dev); + + struct i2c_adapter adapter; + struct i2c_client *ara; + struct i2c_smbus_alert_setup alert_data; + + struct phytium_pci_i2c *controller; + + unsigned int status; + int cmd_err; + u32 abort_source; + + struct i2c_msg *msgs; + int msgs_num; + int msg_write_idx; + int msg_read_idx; + int msg_err; + u32 tx_buf_len; + u8 *tx_buf; + u32 rx_buf_len; + u8 *rx_buf; + + u32 master_cfg; + u32 slave_cfg; + u32 functionality; + unsigned int tx_fifo_depth; + unsigned int rx_fifo_depth; + int rx_outstanding; + + struct i2c_timings timings; + u32 sda_hold_time; + u16 ss_hcnt; + u16 ss_lcnt; + u16 fs_hcnt; + u16 fs_lcnt; + u16 fp_hcnt; + u16 fp_lcnt; + u16 hs_hcnt; + u16 hs_lcnt; + + bool pm_disabled; + void (*disable)(struct phytium_i2c_dev *dev); + void (*disable_int)(struct phytium_i2c_dev *dev); + int (*init)(struct phytium_i2c_dev *dev); +}; + +#define ACCESS_INTR_MASK 0x00000004 + +#define DEFAULT_CLOCK_FREQUENCY 48000000 + +u32 phytium_readl(struct phytium_i2c_dev *dev, int offset); +void phytium_writel(struct phytium_i2c_dev *dev, u32 b, int offset); +unsigned long i2c_phytium_clk_rate(struct phytium_i2c_dev *dev); +int i2c_phytium_prepare_clk(struct phytium_i2c_dev *dev, bool prepare); +int i2c_phytium_wait_bus_not_busy(struct phytium_i2c_dev *dev); +int i2c_phytium_handle_tx_abort(struct phytium_i2c_dev *dev); +u32 i2c_phytium_func(struct i2c_adapter *adap); +void i2c_phytium_disable(struct phytium_i2c_dev *dev); +void i2c_phytium_disable_int(struct phytium_i2c_dev *dev); +int i2c_phytium_set_sda_hold(struct phytium_i2c_dev *dev); +u32 i2c_phytium_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset); +u32 i2c_phytium_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset); + +static inline void __i2c_phytium_enable(struct phytium_i2c_dev *dev) +{ + phytium_writel(dev, 1, IC_ENABLE); +} + +static inline void __i2c_phytium_disable_nowait(struct phytium_i2c_dev *dev) +{ + phytium_writel(dev, 0, IC_ENABLE); +} + +void __i2c_phytium_disable(struct phytium_i2c_dev *dev); + +extern int i2c_phytium_probe(struct phytium_i2c_dev *dev); + +extern int i2c_phytium_probe_slave(struct phytium_i2c_dev *dev); diff --git a/target/linux/phytium/files-5.10/drivers/i2c/busses/i2c-phytium-master.c b/target/linux/phytium/files-5.10/drivers/i2c/busses/i2c-phytium-master.c new file mode 100644 index 000000000..9cab52502 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/i2c/busses/i2c-phytium-master.c @@ -0,0 +1,578 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Phytium I2C adapter driver. + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "i2c-phytium-core.h" + +static int i2c_phytium_init_master(struct phytium_i2c_dev *dev) +{ + /* Disable the adapter */ + __i2c_phytium_disable(dev); + + /* Write standard speed timing parameters */ + phytium_writel(dev, dev->ss_hcnt, IC_SS_SCL_HCNT); + phytium_writel(dev, dev->ss_lcnt, IC_SS_SCL_LCNT); + + /* Write fast mode/fast mode plus timing parameters */ + phytium_writel(dev, dev->fs_hcnt, IC_FS_SCL_HCNT); + phytium_writel(dev, dev->fs_lcnt, IC_FS_SCL_LCNT); + + /* Write high speed timing parameters if supported */ + if (dev->hs_hcnt && dev->hs_hcnt) { + phytium_writel(dev, dev->hs_hcnt, IC_HS_SCL_HCNT); + phytium_writel(dev, dev->hs_lcnt, IC_HS_SCL_LCNT); + } + + /* Write SDA hold time if supported */ + if (dev->sda_hold_time) + phytium_writel(dev, dev->sda_hold_time, IC_SDA_HOLD); + + /* Configure Tx/Rx FIFO threshold levels */ + phytium_writel(dev, dev->tx_fifo_depth >> 1, IC_TX_TL); + phytium_writel(dev, 0, IC_RX_TL); + + /* Configure the I2C master */ + phytium_writel(dev, dev->master_cfg, IC_CON); + + return 0; +} + +static void i2c_phytium_xfer_init(struct phytium_i2c_dev *dev) +{ + struct i2c_msg *msgs = dev->msgs; + u32 ic_con, ic_tar = 0; + + /* Disable the adapter */ + __i2c_phytium_disable(dev); + + /* If the slave address is 10-bit address, enable 10BITADDR */ + ic_con = phytium_readl(dev, IC_CON); + if (msgs[dev->msg_write_idx].flags & I2C_M_TEN) { + ic_con |= IC_CON_10BITADDR_MASTER; + ic_tar = IC_TAR_10BITADDR_MASTER; + } else { + ic_con &= ~IC_CON_10BITADDR_MASTER; + } + + phytium_writel(dev, ic_con, IC_CON); + + /* + * Set the slave (target) address and enable 10-bit addressing mode + * if applicable. + */ + phytium_writel(dev, msgs[dev->msg_write_idx].addr | ic_tar, IC_TAR); + + /* Enforce disabled interrupts */ + i2c_phytium_disable_int(dev); + + /* Enable the adapter */ + __i2c_phytium_enable(dev); + + /* Dummy read */ + phytium_readl(dev, IC_ENABLE_STATUS); + + /* Clear and enable interrupts */ + phytium_readl(dev, IC_CLR_INTR); + phytium_writel(dev, IC_INTR_SMBUS_MASK, IC_INTR_MASK); +} + +static void i2c_phytium_xfer_msg(struct phytium_i2c_dev *dev) +{ + struct i2c_msg *msgs = dev->msgs; + u32 intr_mask; + int tx_limit, rx_limit; + u32 addr = msgs[dev->msg_write_idx].addr; + u32 buf_len = dev->tx_buf_len; + u8 *buf = dev->tx_buf; + bool need_restart = false; + + intr_mask = IC_INTR_MASTER_MASK; + + for (; dev->msg_write_idx < dev->msgs_num; dev->msg_write_idx++) { + u32 flags = msgs[dev->msg_write_idx].flags; + + if (msgs[dev->msg_write_idx].addr != addr) { + dev_err(dev->dev, + "%s: invalid target address\n", __func__); + dev->msg_err = -EINVAL; + break; + } + + if (!(dev->status & STATUS_WRITE_IN_PROGRESS)) { + /* new i2c_msg */ + buf = msgs[dev->msg_write_idx].buf; + buf_len = msgs[dev->msg_write_idx].len; + + if ((dev->master_cfg & IC_CON_RESTART_EN) && + (dev->msg_write_idx > 0)) + need_restart = true; + } + + tx_limit = dev->tx_fifo_depth - phytium_readl(dev, IC_TXFLR); + rx_limit = dev->tx_fifo_depth - phytium_readl(dev, IC_RXFLR); + + while (buf_len > 0 && tx_limit > 0 && rx_limit > 0) { + u32 cmd = 0; + + if (dev->msg_write_idx == dev->msgs_num - 1 && + buf_len == 1 && !(flags & I2C_M_RECV_LEN)) + cmd |= BIT(9); + + if (need_restart) { + cmd |= BIT(10); + need_restart = false; + } + + if (msgs[dev->msg_write_idx].flags & I2C_M_RD) { + /* avoid rx buffer overrun */ + if (dev->rx_outstanding >= dev->rx_fifo_depth) + break; + + phytium_writel(dev, cmd | 0x100, IC_DATA_CMD); + rx_limit--; + dev->rx_outstanding++; + } else { + phytium_writel(dev, cmd | *buf++, IC_DATA_CMD); + } + tx_limit--; + buf_len--; + } + + dev->tx_buf = buf; + dev->tx_buf_len = buf_len; + + /* + * Because we don't know the buffer length in the + * I2C_FUNC_SMBUS_BLOCK_DATA case, we can't stop + * the transaction here. + */ + if (buf_len > 0 || flags & I2C_M_RECV_LEN) { + /* more bytes to be written */ + dev->status |= STATUS_WRITE_IN_PROGRESS; + break; + } + + dev->status &= ~STATUS_WRITE_IN_PROGRESS; + } + + if (dev->msg_write_idx == dev->msgs_num) + intr_mask &= ~IC_INTR_TX_EMPTY; + + if (dev->msg_err) + intr_mask = 0; + + phytium_writel(dev, intr_mask, IC_INTR_MASK); +} + +static u8 i2c_phytium_recv_len(struct phytium_i2c_dev *dev, u8 len) +{ + struct i2c_msg *msgs = dev->msgs; + u32 flags = msgs[dev->msg_read_idx].flags; + + /* + * Adjust the buffer length and mask the flag + * after receiving the first byte. + */ + len += (flags & I2C_CLIENT_PEC) ? 2 : 1; + dev->tx_buf_len = len - min_t(u8, len, dev->rx_outstanding); + msgs[dev->msg_read_idx].len = len; + msgs[dev->msg_read_idx].flags &= ~I2C_M_RECV_LEN; + + return len; +} + +static void i2c_phytium_read(struct phytium_i2c_dev *dev) +{ + struct i2c_msg *msgs = dev->msgs; + int rx_valid; + + for (; dev->msg_read_idx < dev->msgs_num; dev->msg_read_idx++) { + u32 len; + u8 *buf; + + if (!(msgs[dev->msg_read_idx].flags & I2C_M_RD)) + continue; + + if (!(dev->status & STATUS_READ_IN_PROGRESS)) { + len = msgs[dev->msg_read_idx].len; + buf = msgs[dev->msg_read_idx].buf; + } else { + len = dev->rx_buf_len; + buf = dev->rx_buf; + } + + rx_valid = phytium_readl(dev, IC_RXFLR); + + for (; len > 0 && rx_valid > 0; len--, rx_valid--) { + u32 flags = msgs[dev->msg_read_idx].flags; + + *buf = phytium_readl(dev, IC_DATA_CMD); + /* Ensure length byte is a valid value */ + if (flags & I2C_M_RECV_LEN && + *buf <= I2C_SMBUS_BLOCK_MAX && *buf > 0) { + len = i2c_phytium_recv_len(dev, *buf); + } + buf++; + dev->rx_outstanding--; + } + + if (len > 0) { + dev->status |= STATUS_READ_IN_PROGRESS; + dev->rx_buf_len = len; + dev->rx_buf = buf; + break; + } + + dev->status &= ~STATUS_READ_IN_PROGRESS; + } +} + +static int i2c_phytium_xfer(struct i2c_adapter *adapter, struct i2c_msg msgs[], int num) +{ + struct phytium_i2c_dev *dev = i2c_get_adapdata(adapter); + int ret; + + dev_dbg(dev->dev, "%s: msgs: %d\n", __func__, num); + + pm_runtime_get_sync(dev->dev); + + reinit_completion(&dev->cmd_complete); + dev->msgs = msgs; + dev->msgs_num = num; + dev->cmd_err = 0; + dev->msg_write_idx = 0; + dev->msg_read_idx = 0; + dev->msg_err = 0; + dev->status = STATUS_IDLE; + dev->abort_source = 0; + dev->rx_outstanding = 0; + + ret = i2c_phytium_wait_bus_not_busy(dev); + if (ret < 0) + goto done; + + /* Start the transfers */ + i2c_phytium_xfer_init(dev); + + /* Wait for tx to complete */ + if (!wait_for_completion_timeout(&dev->cmd_complete, adapter->timeout)) { + dev_err(dev->dev, "controller timed out\n"); + i2c_recover_bus(&dev->adapter); + i2c_phytium_init_master(dev); + ret = -ETIMEDOUT; + goto done; + } + + __i2c_phytium_disable_nowait(dev); + + if (dev->msg_err) { + ret = dev->msg_err; + goto done; + } + + if (likely(!dev->cmd_err && !dev->status)) { + ret = num; + goto done; + } + + /* We have got an error */ + if (dev->cmd_err == IC_ERR_TX_ABRT) { + ret = i2c_phytium_handle_tx_abort(dev); + goto done; + } + + if (dev->status) + dev_err(dev->dev, "transfer terminated early.\n"); + + ret = -EIO; + +done: + pm_runtime_mark_last_busy(dev->dev); + pm_runtime_put_autosuspend(dev->dev); + + return ret; +} + +static const struct i2c_algorithm i2c_phytium_algo = { + .master_xfer = i2c_phytium_xfer, + .functionality = i2c_phytium_func, +}; + +static const struct i2c_adapter_quirks i2c_phytium_quirks = { + .flags = I2C_AQ_NO_ZERO_LEN, +}; + +static u32 i2c_phytium_read_clear_intrbits(struct phytium_i2c_dev *dev) +{ + u32 stat; + + stat = phytium_readl(dev, IC_INTR_STAT); + + if (stat & IC_INTR_RX_UNDER) + phytium_readl(dev, IC_CLR_RX_UNDER); + if (stat & IC_INTR_RX_OVER) + phytium_readl(dev, IC_CLR_RX_OVER); + if (stat & IC_INTR_TX_OVER) + phytium_readl(dev, IC_CLR_TX_OVER); + if (stat & IC_INTR_RD_REQ) + phytium_readl(dev, IC_CLR_RD_REQ); + if (stat & IC_INTR_TX_ABRT) { + dev->abort_source = phytium_readl(dev, IC_TX_ABRT_SOURCE); + phytium_readl(dev, IC_CLR_TX_ABRT); + } + if (stat & IC_INTR_RX_DONE) + phytium_readl(dev, IC_CLR_RX_DONE); + if (stat & IC_INTR_ACTIVITY) + phytium_readl(dev, IC_CLR_ACTIVITY); + if (stat & IC_INTR_STOP_DET) + phytium_readl(dev, IC_CLR_STOP_DET); + if (stat & IC_INTR_START_DET) + phytium_readl(dev, IC_CLR_START_DET); + if (stat & IC_INTR_GEN_CALL) + phytium_readl(dev, IC_CLR_GEN_CALL); + if (stat & IC_INTR_SMBCLK_EXT_LOW_TIMEOUT) + phytium_readl(dev, IC_CLR_SMBCLK_EXT_LOW_TIMEOUT); + if (stat & IC_INTR_SMBCLK_TMO_LOW_TIMEOUT) + phytium_readl(dev, IC_CLR_SMBCLK_TMO_LOW_TIMEOUT); + if (stat & IC_INTR_SMBSDA_LOW_TIMEOUT) + phytium_readl(dev, IC_CLR_SMBDAT_LOW_TIMEOUT); + if (stat & IC_INTR_SMBALERT_IN_N) + phytium_readl(dev, IC_CLR_SMBALERT_IN_N); + + return stat; +} + +static int i2c_phytium_irq_handler_master(struct phytium_i2c_dev *dev) +{ + u32 stat; + + stat = i2c_phytium_read_clear_intrbits(dev); + + /* SMBus interrupt */ + if (stat & (IC_INTR_SMBCLK_EXT_LOW_TIMEOUT | IC_INTR_SMBCLK_TMO_LOW_TIMEOUT)) { + phytium_writel(dev, phytium_readl(dev, IC_ENABLE) & (~BIT(6)), + IC_ENABLE); + phytium_writel(dev, phytium_readl(dev, IC_ENABLE) | BIT(4), + IC_ENABLE); + goto abort; + } + + if (stat & IC_INTR_SMBSDA_LOW_TIMEOUT) { + phytium_writel(dev, phytium_readl(dev, IC_ENABLE) | BIT(6), + IC_ENABLE); + goto abort; + } + + if (stat & IC_INTR_SMBALERT_IN_N && dev->ara) + i2c_handle_smbus_alert(dev->ara); + + if (stat & IC_INTR_TX_ABRT) { + dev->cmd_err |= IC_ERR_TX_ABRT; + dev->status = STATUS_IDLE; + + /* Anytime TX_ABRT is set, the contents of the tx/rx + * buffers are flushed. Make sure to skip them. + */ + phytium_writel(dev, 0, IC_INTR_MASK); + goto abort; + } + + if (stat & IC_INTR_RX_FULL) + i2c_phytium_read(dev); + + if (stat & IC_INTR_TX_EMPTY) + i2c_phytium_xfer_msg(dev); + +abort: + if ((stat & (IC_INTR_TX_ABRT | IC_INTR_STOP_DET)) || + dev->msg_err) + complete(&dev->cmd_complete); + else if (unlikely(dev->flags & ACCESS_INTR_MASK)) { + /* Workaround to trigger pending interrupt */ + stat = phytium_readl(dev, IC_INTR_MASK); + i2c_phytium_disable_int(dev); + phytium_writel(dev, stat, IC_INTR_MASK); + } + + return 0; +} + +static int i2c_phytium_set_timings_master(struct phytium_i2c_dev *dev) +{ + const char *mode_str, *fp_str = ""; + u32 sda_falling_time, scl_falling_time; + struct i2c_timings *t = &dev->timings; + u32 ic_clk; + int ret; + + /* Set standard and fast speed dividers for high/low periods */ + sda_falling_time = t->sda_fall_ns ?: 300; /* ns */ + scl_falling_time = t->scl_fall_ns ?: 300; /* ns */ + + /* Calculate SCL timing parameters for standard mode if not set */ + if (!dev->ss_hcnt || !dev->ss_lcnt) { + ic_clk = i2c_phytium_clk_rate(dev); + dev->ss_hcnt = + i2c_phytium_scl_hcnt(ic_clk, + 4000, /* tHD;STA = tHIGH = 4.0 us */ + sda_falling_time, + 0, /* 0: DW default, 1: Ideal */ + 0); /* No offset */ + dev->ss_lcnt = + i2c_phytium_scl_lcnt(ic_clk, + 4700, /* tLOW = 4.7 us */ + scl_falling_time, + 0); /* No offset */ + } + dev_dbg(dev->dev, "Standard Mode HCNT:LCNT = %d:%d\n", + dev->ss_hcnt, dev->ss_lcnt); + /* + * Set SCL timing parameters for fast mode or fast mode plus. Only + * difference is the timing parameter values since the registers are + * the same. + */ + if (t->bus_freq_hz == 1000000) { + /* + * Check are fast mode plus parameters available and use + * fast mode if not. + */ + if (dev->fp_hcnt && dev->fp_lcnt) { + dev->fs_hcnt = dev->fp_hcnt; + dev->fs_lcnt = dev->fp_lcnt; + fp_str = " Plus"; + } + } + /* + * Calculate SCL timing parameters for fast mode if not set. They are + * needed also in high speed mode. + */ + if (!dev->fs_hcnt || !dev->fs_lcnt) { + ic_clk = i2c_phytium_clk_rate(dev); + dev->fs_hcnt = + i2c_phytium_scl_hcnt(ic_clk, + 600, /* tHD;STA = tHIGH = 0.6 us */ + sda_falling_time, + 0, /* 0: DW default, 1: Ideal */ + 0); /* No offset */ + dev->fs_lcnt = + i2c_phytium_scl_lcnt(ic_clk, + 1300, /* tLOW = 1.3 us */ + scl_falling_time, + 0); /* No offset */ + } + dev_dbg(dev->dev, "Fast Mode%s HCNT:LCNT = %d:%d\n", + fp_str, dev->fs_hcnt, dev->fs_lcnt); + + if (dev->hs_hcnt && dev->hs_lcnt) + dev_dbg(dev->dev, "High Speed Mode HCNT:LCNT = %d:%d\n", + dev->hs_hcnt, dev->hs_lcnt); + + ret = i2c_phytium_set_sda_hold(dev); + if (ret) + goto out; + + switch (dev->master_cfg & IC_CON_SPEED_MASK) { + case IC_CON_SPEED_STD: + mode_str = "Standard Mode"; + break; + case IC_CON_SPEED_HIGH: + mode_str = "High Speed Mode"; + break; + default: + mode_str = "Fast Mode"; + } + dev_dbg(dev->dev, "Bus speed: %s%s\n", mode_str, fp_str); + +out: + return ret; +} + +static irqreturn_t i2c_phytium_isr(int this_irq, void *dev_id) +{ + struct phytium_i2c_dev *dev = dev_id; + u32 stat, enabled; + + enabled = phytium_readl(dev, IC_ENABLE); + stat = phytium_readl(dev, IC_RAW_INTR_STAT); + if (!enabled || !(stat & ~IC_INTR_ACTIVITY)) + return IRQ_NONE; + + i2c_phytium_irq_handler_master(dev); + + return IRQ_HANDLED; +} + +int i2c_phytium_probe(struct phytium_i2c_dev *dev) +{ + struct i2c_adapter *adapter = &dev->adapter; + unsigned long irq_flags; + int ret; + + init_completion(&dev->cmd_complete); + + dev->init = i2c_phytium_init_master; + dev->disable = i2c_phytium_disable; + dev->disable_int = i2c_phytium_disable_int; + + ret = i2c_phytium_set_timings_master(dev); + if (ret) + return ret; + + ret = dev->init(dev); + if (ret) + return ret; + + /* XXX: should be initialized in firmware, remove it in future */ +#define DEFAULT_TIMEOUT (DEFAULT_CLOCK_FREQUENCY / 1000 * 35) + phytium_writel(dev, DEFAULT_TIMEOUT, IC_SMBCLK_LOW_MEXT); + phytium_writel(dev, DEFAULT_TIMEOUT, IC_SMBCLK_LOW_TIMEOUT); + phytium_writel(dev, DEFAULT_TIMEOUT, IC_SMBDAT_STUCK_TIMEOUT); + + snprintf(adapter->name, sizeof(adapter->name), "Phytium I2C adapter"); + adapter->retries = 3; + adapter->algo = &i2c_phytium_algo; + adapter->quirks = &i2c_phytium_quirks; + adapter->dev.parent = dev->dev; + i2c_set_adapdata(adapter, dev); + + irq_flags = IRQF_SHARED | IRQF_COND_SUSPEND; + + i2c_phytium_disable_int(dev); + ret = devm_request_irq(dev->dev, dev->irq, i2c_phytium_isr, irq_flags, + dev_name(dev->dev), dev); + if (ret) { + dev_err(dev->dev, "failed to request irq %i: %d\n", dev->irq, ret); + return ret; + } + + /* + * Increment PM usage count during adapter registration in order to + * avoid possible spurious runtime suspend when adapter device is + * registered to the device core and immediate resume in case bus has + * registered I2C slaves that do I2C transfers in their probe. + */ + pm_runtime_get_noresume(dev->dev); + ret = i2c_add_numbered_adapter(adapter); + if (ret) + dev_err(dev->dev, "fail to add adapter: %d\n", ret); + pm_runtime_put_noidle(dev->dev); + + return ret; +} +EXPORT_SYMBOL_GPL(i2c_phytium_probe); + +MODULE_DESCRIPTION("Phytium I2C bus master adapter"); +MODULE_LICENSE("GPL"); diff --git a/target/linux/phytium/files-5.10/drivers/i2c/busses/i2c-phytium-pci.c b/target/linux/phytium/files-5.10/drivers/i2c/busses/i2c-phytium-pci.c new file mode 100644 index 000000000..b0482b82b --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/i2c/busses/i2c-phytium-pci.c @@ -0,0 +1,240 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * PCI driver for Phytium I2C adapter. + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "i2c-phytium-core.h" + +#define DRV_NAME "i2c-phytium-pci" + +enum phytium_pci_ctl_id_t { + octopus_i2c, +}; + +struct scl_sda_cfg { + u32 ss_hcnt; + u32 fs_hcnt; + u32 ss_lcnt; + u32 fs_lcnt; + u32 sda_hold; +}; + +struct phytium_pci_i2c { + u32 bus_num; + u32 bus_cfg; + u32 tx_fifo_depth; + u32 rx_fifo_depth; + u32 clk_khz; + u32 functionality; + u32 flags; + struct scl_sda_cfg *scl_sda_cfg; + int (*setup)(struct pci_dev *pdev, struct phytium_pci_i2c *c); +}; + +/* Octopus HCNT/LCNT/SDA hold time */ +static struct scl_sda_cfg octopus_config = { + .ss_hcnt = 0x190, + .ss_lcnt = 0x1d6, + .fs_hcnt = 0x3c, + .fs_lcnt = 0x82, + .sda_hold = 0x0, // XXX +}; + +static int octopus_setup(struct pci_dev *pdev, struct phytium_pci_i2c *c) +{ + struct phytium_i2c_dev *i2c = pci_get_drvdata(pdev); + struct i2c_client *ara; + + if (pdev->device == 0xdc32) { + /* + * Since we have already register the adapter, the dev->irq + * must be valid. + */ + i2c->alert_data.irq = i2c->irq; + + ara = i2c_new_smbus_alert_device(&i2c->adapter, &i2c->alert_data); + if (IS_ERR(ara)) + return PTR_ERR(ara); + + i2c->ara = ara; + } + + return 0; +} + +static struct phytium_pci_i2c pci_ctrl_info[] = { + [octopus_i2c] = { + .bus_num = -1, + .bus_cfg = IC_CON_MASTER | IC_CON_SLAVE_DISABLE | + IC_CON_RESTART_EN | IC_CON_SPEED_FAST, + .tx_fifo_depth = 7, + .rx_fifo_depth = 7, + .functionality = I2C_FUNC_10BIT_ADDR, + .clk_khz = 48000000, + .scl_sda_cfg = &octopus_config, + .setup = octopus_setup, + }, +}; + +#ifdef CONFIG_PM +static int i2c_phytium_pci_suspend(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct phytium_i2c_dev *i_dev = pci_get_drvdata(pdev); + + i_dev->disable(i_dev); + + return 0; +} + +static int i2c_phytium_pci_resume(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct phytium_i2c_dev *i_dev = pci_get_drvdata(pdev); + + return i_dev->init(i_dev); +} +#endif + +static UNIVERSAL_DEV_PM_OPS(i2c_phytium_pm_ops, i2c_phytium_pci_suspend, + i2c_phytium_pci_resume, NULL); + +static u32 i2c_phytium_get_clk_rate_khz(struct phytium_i2c_dev *dev) +{ + return dev->controller->clk_khz; +} + +static int i2c_phytium_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct phytium_i2c_dev *dev; + struct i2c_adapter *adapter; + struct phytium_pci_i2c *controller; + struct scl_sda_cfg *cfg; + int ret; + + if (id->driver_data >= ARRAY_SIZE(pci_ctrl_info)) { + dev_err(&pdev->dev, "%s: invalid driver data %ld\n", __func__, + id->driver_data); + ret = -EINVAL; + goto out; + } + + controller = &pci_ctrl_info[id->driver_data]; + + ret = pcim_enable_device(pdev); + if (ret) { + dev_err(&pdev->dev, "Failed to enable I2C PCI device (%d)\n", ret); + goto out; + } + + ret = pcim_iomap_regions(pdev, 0x1, pci_name(pdev)); + if (ret) { + dev_err(&pdev->dev, "I/O memory remapping failed\n"); + goto out; + } + + dev = devm_kzalloc(&pdev->dev, sizeof(struct phytium_i2c_dev), GFP_KERNEL); + if (!dev) { + ret = -ENOMEM; + goto out; + } + + dev->controller = controller; + dev->get_clk_rate_khz = i2c_phytium_get_clk_rate_khz; + dev->base = pcim_iomap_table(pdev)[0]; + dev->dev = &pdev->dev; + dev->irq = pdev->irq; + dev->flags |= controller->flags; + + dev->functionality = controller->functionality | IC_DEFAULT_FUNCTIONALITY; + dev->master_cfg = controller->bus_cfg; + if (controller->scl_sda_cfg) { + cfg = controller->scl_sda_cfg; + dev->ss_hcnt = cfg->ss_hcnt; + dev->fs_hcnt = cfg->fs_hcnt; + dev->ss_lcnt = cfg->ss_lcnt; + dev->fs_lcnt = cfg->fs_lcnt; + dev->sda_hold_time = cfg->sda_hold; + } + + pci_set_drvdata(pdev, dev); + + dev->tx_fifo_depth = controller->tx_fifo_depth; + dev->rx_fifo_depth = controller->rx_fifo_depth; + + adapter = &dev->adapter; + adapter->owner = THIS_MODULE; + adapter->class = 0; + ACPI_COMPANION_SET(&adapter->dev, ACPI_COMPANION(&pdev->dev)); + adapter->nr = controller->bus_num; + + ret = i2c_phytium_probe(dev); + if (ret) + goto out; + + if (controller->setup) { + ret = controller->setup(pdev, controller); + if (ret) + goto out; + } + + pm_runtime_set_autosuspend_delay(&pdev->dev, 1000); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_put_autosuspend(&pdev->dev); + pm_runtime_allow(&pdev->dev); + +out: + return ret; +} + +static void i2c_phytium_pci_remove(struct pci_dev *pdev) +{ + struct phytium_i2c_dev *dev = pci_get_drvdata(pdev); + + dev->disable(dev); + pm_runtime_forbid(&pdev->dev); + pm_runtime_get_noresume(&pdev->dev); + + i2c_del_adapter(&dev->adapter); +} + +static const struct pci_device_id i2_phytium_pci_ids[] = { + { PCI_DEVICE(0x1db7, 0xdc32), 0, 0, octopus_i2c }, + { PCI_DEVICE(0x1db7, 0xdc30), 0, 0, octopus_i2c }, + { } +}; +MODULE_DEVICE_TABLE(pci, i2_phytium_pci_ids); + +static struct pci_driver phytium_i2c_driver = { + .name = DRV_NAME, + .id_table = i2_phytium_pci_ids, + .probe = i2c_phytium_pci_probe, + .remove = i2c_phytium_pci_remove, + .driver = { + .pm = &i2c_phytium_pm_ops, + }, +}; + +module_pci_driver(phytium_i2c_driver); + +MODULE_ALIAS("i2c-phytium-pci"); +MODULE_AUTHOR("Cheng Quan "); +MODULE_DESCRIPTION("Phytium PCI I2C bus adapter"); +MODULE_LICENSE("GPL"); diff --git a/target/linux/phytium/files-5.10/drivers/i2c/busses/i2c-phytium-platform.c b/target/linux/phytium/files-5.10/drivers/i2c/busses/i2c-phytium-platform.c new file mode 100644 index 000000000..cb13c287a --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/i2c/busses/i2c-phytium-platform.c @@ -0,0 +1,364 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Phytium I2C adapter driver. + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "i2c-phytium-core.h" + +#define DRV_NAME "i2c-phytium-platform" + +static u32 i2c_phytium_get_clk_rate_khz(struct phytium_i2c_dev *dev) +{ + return clk_get_rate(dev->clk)/1000; +} + +#ifdef CONFIG_ACPI +static void phytium_i2c_acpi_params(struct platform_device *pdev, char method[], + u16 *hcnt, u16 *lcnt, u32 *sda_hold) +{ + struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER }; + acpi_handle handle = ACPI_HANDLE(&pdev->dev); + union acpi_object *obj; + + if (ACPI_FAILURE(acpi_evaluate_object(handle, method, NULL, &buf))) + return; + + obj = (union acpi_object *)buf.pointer; + if (obj->type == ACPI_TYPE_PACKAGE && obj->package.count == 3) { + const union acpi_object *objs = obj->package.elements; + + *hcnt = (u16)objs[0].integer.value; + *lcnt = (u16)objs[1].integer.value; + *sda_hold = (u32)objs[2].integer.value; + } + + kfree(buf.pointer); +} + +static int phytium_i2c_acpi_configure(struct platform_device *pdev) +{ + struct phytium_i2c_dev *dev = platform_get_drvdata(pdev); + struct i2c_timings *t = &dev->timings; + u32 ss_ht = 0, fp_ht = 0, hs_ht = 0, fs_ht = 0; + acpi_handle handle = ACPI_HANDLE(&pdev->dev); + const struct acpi_device_id *id; + struct acpi_device *adev; + + dev->adapter.nr = -1; + dev->tx_fifo_depth = 32; + dev->rx_fifo_depth = 32; + + /* + * Try to get SDA hold time and *CNT values from an ACPI method for + * selected speed modes. + */ + phytium_i2c_acpi_params(pdev, "SSCN", &dev->ss_hcnt, &dev->ss_lcnt, &ss_ht); + phytium_i2c_acpi_params(pdev, "FPCN", &dev->fp_hcnt, &dev->fp_lcnt, &fp_ht); + phytium_i2c_acpi_params(pdev, "HSCN", &dev->hs_hcnt, &dev->hs_lcnt, &hs_ht); + phytium_i2c_acpi_params(pdev, "FMCN", &dev->fs_hcnt, &dev->fs_lcnt, &fs_ht); + + switch (t->bus_freq_hz) { + case 100000: + dev->sda_hold_time = ss_ht; + break; + case 1000000: + dev->sda_hold_time = fp_ht; + break; + case 3400000: + dev->sda_hold_time = hs_ht; + break; + case 400000: + default: + dev->sda_hold_time = fs_ht; + break; + } + + id = acpi_match_device(pdev->dev.driver->acpi_match_table, &pdev->dev); + if (id && id->driver_data) + dev->flags |= (u32)id->driver_data; + + if (acpi_bus_get_device(handle, &adev)) + return -ENODEV; + + return 0; +} + +static const struct acpi_device_id phytium_i2c_acpi_match[] = { + { "PHYT0038", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, phytium_i2c_acpi_match); +#else +static inline int phytium_i2c_acpi_configure(struct platform_device *pdev) +{ + return -ENODEV; +} +#endif + +static void i2c_phytium_configure_master(struct phytium_i2c_dev *dev) +{ + struct i2c_timings *t = &dev->timings; + + dev->functionality = I2C_FUNC_10BIT_ADDR | IC_DEFAULT_FUNCTIONALITY; + + dev->master_cfg = IC_CON_MASTER | IC_CON_SLAVE_DISABLE | + IC_CON_RESTART_EN; + + dev->mode = PHYTIUM_IC_MASTER; + + switch (t->bus_freq_hz) { + case 100000: + dev->master_cfg |= IC_CON_SPEED_STD; + break; + case 3400000: + dev->master_cfg |= IC_CON_SPEED_HIGH; + break; + default: + dev->master_cfg |= IC_CON_SPEED_FAST; + } +} + +static void i2c_phytium_configure_slave(struct phytium_i2c_dev *dev) +{ + dev->functionality = I2C_FUNC_SLAVE | IC_DEFAULT_FUNCTIONALITY; + + dev->slave_cfg = IC_CON_RX_FIFO_FULL_HLD_CTRL | + IC_CON_RESTART_EN | IC_CON_STOP_DET_IFADDRESSED; + + dev->mode = PHYTIUM_IC_SLAVE; +} + +static int phytium_i2c_plat_probe(struct platform_device *pdev) +{ + struct i2c_adapter *adap; + struct phytium_i2c_dev *dev; + struct i2c_timings *t; + u32 acpi_speed; + struct resource *mem; + int irq, ret, i; + static const int supported_speeds[] = { + 0, 100000, 400000, 1000000, 3400000 + }; + + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + dev = devm_kzalloc(&pdev->dev, sizeof(struct phytium_i2c_dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + dev->base = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(dev->base)) + return PTR_ERR(dev->base); + + dev->dev = &pdev->dev; + dev->irq = irq; + platform_set_drvdata(pdev, dev); + + dev->rst = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL); + if (IS_ERR(dev->rst)) { + if (PTR_ERR(dev->rst) == -EPROBE_DEFER) + return -EPROBE_DEFER; + } else { + reset_control_deassert(dev->rst); + } + + t = &dev->timings; + i2c_parse_fw_timings(&pdev->dev, t, false); + + acpi_speed = i2c_acpi_find_bus_speed(&pdev->dev); + /* + * Some DSTDs use a non standard speed, round down to the lowest + * standard speed. + */ + for (i = 1; i < ARRAY_SIZE(supported_speeds); i++) { + if (acpi_speed < supported_speeds[i]) + break; + } + acpi_speed = supported_speeds[i - 1]; + + /* + * Find bus speed from the "clock-frequency" device property, ACPI + * or by using fast mode if neither is set. + */ + if (acpi_speed && t->bus_freq_hz) + t->bus_freq_hz = min(t->bus_freq_hz, acpi_speed); + else if (acpi_speed || t->bus_freq_hz) + t->bus_freq_hz = max(t->bus_freq_hz, acpi_speed); + else + t->bus_freq_hz = 400000; + + if (has_acpi_companion(&pdev->dev)) + phytium_i2c_acpi_configure(pdev); + + /* + * Only standard mode at 100kHz, fast mode at 400kHz, + * fast mode plus at 1MHz and high speed mode at 3.4MHz are supported. + */ + if (t->bus_freq_hz != 100000 && t->bus_freq_hz != 400000 && + t->bus_freq_hz != 1000000 && t->bus_freq_hz != 3400000) { + dev_err(&pdev->dev, + "%d Hz is unsupported, only 100kHz, 400kHz, 1MHz and 3.4MHz are supported\n", + t->bus_freq_hz); + ret = -EINVAL; + goto exit_reset; + } + + if (i2c_detect_slave_mode(&pdev->dev)) + i2c_phytium_configure_slave(dev); + else + i2c_phytium_configure_master(dev); + + dev->clk = devm_clk_get(&pdev->dev, NULL); + if (!i2c_phytium_prepare_clk(dev, true)) { + u64 clk_khz; + + dev->get_clk_rate_khz = i2c_phytium_get_clk_rate_khz; + clk_khz = dev->get_clk_rate_khz(dev); + + if (!dev->sda_hold_time && t->sda_hold_ns) + dev->sda_hold_time = + div_u64(clk_khz * t->sda_hold_ns + 500000, 1000000); + } + + dev->tx_fifo_depth = 7; + dev->rx_fifo_depth = 7; + dev->adapter.nr = pdev->id; + + adap = &dev->adapter; + adap->owner = THIS_MODULE; + adap->class = I2C_CLASS_DEPRECATED; + ACPI_COMPANION_SET(&adap->dev, ACPI_COMPANION(&pdev->dev)); + adap->dev.of_node = pdev->dev.of_node; + + dev_pm_set_driver_flags(&pdev->dev, + DPM_FLAG_SMART_PREPARE | + DPM_FLAG_SMART_SUSPEND | + DPM_FLAG_MAY_SKIP_RESUME); + + /* The code below assumes runtime PM to be disabled. */ + WARN_ON(pm_runtime_enabled(&pdev->dev)); + + pm_runtime_set_autosuspend_delay(&pdev->dev, 1000); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_set_active(&pdev->dev); + + pm_runtime_enable(&pdev->dev); + + if (dev->mode == PHYTIUM_IC_SLAVE) + ret = i2c_phytium_probe_slave(dev); + else + ret = i2c_phytium_probe(dev); + + if (ret) + goto exit_probe; + + return ret; + +exit_probe: + pm_runtime_disable(dev->dev); +exit_reset: + if (!IS_ERR_OR_NULL(dev->rst)) + reset_control_assert(dev->rst); + return ret; +} + +static int phytium_i2c_plat_remove(struct platform_device *pdev) +{ + struct phytium_i2c_dev *dev = platform_get_drvdata(pdev); + + pm_runtime_get_sync(&pdev->dev); + + i2c_del_adapter(&dev->adapter); + + dev->disable(dev); + + pm_runtime_dont_use_autosuspend(&pdev->dev); + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(dev->dev); + + if (!IS_ERR_OR_NULL(dev->rst)) + reset_control_assert(dev->rst); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id phytium_i2c_of_match[] = { + { .compatible = "phytium,i2c", }, + {}, +}; +MODULE_DEVICE_TABLE(of, phytium_i2c_of_match); +#endif + +static int __maybe_unused phytium_i2c_plat_suspend(struct device *dev) +{ + struct phytium_i2c_dev *idev = dev_get_drvdata(dev); + + idev->disable(idev); + i2c_phytium_prepare_clk(idev, false); + + return 0; +} + +static int __maybe_unused phytium_i2c_plat_resume(struct device *dev) +{ + struct phytium_i2c_dev *idev = dev_get_drvdata(dev); + + i2c_phytium_prepare_clk(idev, true); + + idev->init(idev); + + return 0; +} + +static const struct dev_pm_ops phytium_i2c_dev_pm_ops = { + SET_LATE_SYSTEM_SLEEP_PM_OPS(phytium_i2c_plat_suspend, + phytium_i2c_plat_resume) + SET_RUNTIME_PM_OPS(phytium_i2c_plat_suspend, + phytium_i2c_plat_resume, NULL) +}; + +static struct platform_driver phytium_i2c_driver = { + .probe = phytium_i2c_plat_probe, + .remove = phytium_i2c_plat_remove, + .driver = { + .name = DRV_NAME, + .of_match_table = of_match_ptr(phytium_i2c_of_match), + .acpi_match_table = ACPI_PTR(phytium_i2c_acpi_match), + .pm = &phytium_i2c_dev_pm_ops, + }, +}; +module_platform_driver(phytium_i2c_driver); + +MODULE_ALIAS("platform:i2c-phytium"); +MODULE_AUTHOR("Chen Baozi "); +MODULE_DESCRIPTION("Phytium I2C bus adapter"); +MODULE_LICENSE("GPL"); diff --git a/target/linux/phytium/files-5.10/drivers/i2c/busses/i2c-phytium-slave.c b/target/linux/phytium/files-5.10/drivers/i2c/busses/i2c-phytium-slave.c new file mode 100644 index 000000000..a9409f55c --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/i2c/busses/i2c-phytium-slave.c @@ -0,0 +1,262 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Phytium I2C adapter driver (slave only). + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "i2c-phytium-core.h" + +static void i2c_phytium_configure_fifo_slave(struct phytium_i2c_dev *dev) +{ + /* Configure Tx/Rx FIFO threshold levels. */ + phytium_writel(dev, 0, IC_TX_TL); + phytium_writel(dev, 0, IC_RX_TL); + + /* Configure the I2C slave. */ + phytium_writel(dev, dev->slave_cfg, IC_CON); + phytium_writel(dev, IC_INTR_SLAVE_MASK, IC_INTR_MASK); +} + +static int i2c_phytium_init_slave(struct phytium_i2c_dev *dev) +{ + /* Disable the adapter. */ + __i2c_phytium_disable(dev); + + /* Write SDA hold time if supported */ + if (dev->sda_hold_time) + phytium_writel(dev, dev->sda_hold_time, IC_SDA_HOLD); + + i2c_phytium_configure_fifo_slave(dev); + + return 0; +} + +static int i2c_phytium_reg_slave(struct i2c_client *slave) +{ + struct phytium_i2c_dev *dev = i2c_get_adapdata(slave->adapter); + + if (dev->slave) + return -EBUSY; + if (slave->flags & I2C_CLIENT_TEN) + return -EAFNOSUPPORT; + pm_runtime_get_sync(dev->dev); + + /* + * Set slave address in the IC_SAR register, + * the address to which the i2c responds. + */ + __i2c_phytium_disable_nowait(dev); + phytium_writel(dev, slave->addr, IC_SAR); + dev->slave = slave; + + __i2c_phytium_enable(dev); + + dev->cmd_err = 0; + dev->msg_write_idx = 0; + dev->msg_read_idx = 0; + dev->msg_err = 0; + dev->status = STATUS_IDLE; + dev->abort_source = 0; + dev->rx_outstanding = 0; + + return 0; +} + +static int i2c_phytium_unreg_slave(struct i2c_client *slave) +{ + struct phytium_i2c_dev *dev = i2c_get_adapdata(slave->adapter); + + dev->disable_int(dev); + dev->disable(dev); + dev->slave = NULL; + pm_runtime_put(dev->dev); + + return 0; +} + +static u32 i2c_phytium_read_clear_intrbits_slave(struct phytium_i2c_dev *dev) +{ + u32 stat; + + /* + * The IC_INTR_STAT register just indicates "enabled" interrupts. + * Ths unmasked raw version of interrupt status bits are available + * in the IC_RAW_INTR_STAT register. + * + * That is, + * stat = phytium_readl(IC_INTR_STAT); + * equals to, + * stat = phytium_readl(IC_RAW_INTR_STAT) & phytium_readl(IC_INTR_MASK); + * + * The raw version might be useful for debugging purposes. + */ + stat = phytium_readl(dev, IC_INTR_STAT); + + /* + * Do not use the IC_CLR_INTR register to clear interrupts, or + * you'll miss some interrupts, triggered during the period from + * phytium_readl(IC_INTR_STAT) to phytium_readl(IC_CLR_INTR). + * + * Instead, use the separately-prepared IC_CLR_* registers. + */ + if (stat & IC_INTR_TX_ABRT) + phytium_readl(dev, IC_CLR_TX_ABRT); + if (stat & IC_INTR_RX_UNDER) + phytium_readl(dev, IC_CLR_RX_UNDER); + if (stat & IC_INTR_RX_OVER) + phytium_readl(dev, IC_CLR_RX_OVER); + if (stat & IC_INTR_TX_OVER) + phytium_readl(dev, IC_CLR_TX_OVER); + if (stat & IC_INTR_RX_DONE) + phytium_readl(dev, IC_CLR_RX_DONE); + if (stat & IC_INTR_ACTIVITY) + phytium_readl(dev, IC_CLR_ACTIVITY); + if (stat & IC_INTR_STOP_DET) + phytium_readl(dev, IC_CLR_STOP_DET); + if (stat & IC_INTR_START_DET) + phytium_readl(dev, IC_CLR_START_DET); + if (stat & IC_INTR_GEN_CALL) + phytium_readl(dev, IC_CLR_GEN_CALL); + + return stat; +} + +/* + * Interrupt service routine. This gets called whenever an I2C slave interrupt + * occurs. + */ +static int i2c_phytium_irq_handler_slave(struct phytium_i2c_dev *dev) +{ + u32 raw_stat, stat, enabled; + u8 val, slave_activity; + + stat = phytium_readl(dev, IC_INTR_STAT); + enabled = phytium_readl(dev, IC_ENABLE); + raw_stat = phytium_readl(dev, IC_RAW_INTR_STAT); + slave_activity = ((phytium_readl(dev, IC_STATUS) & + IC_STATUS_SLAVE_ACTIVITY) >> 6); + + if (!enabled || !(raw_stat & ~IC_INTR_ACTIVITY) || !dev->slave) + return 0; + + dev_dbg(dev->dev, + "%#x STATUS SLAVE_ACTIVITY=%#x : RAW_INTR_STAT=%#x : INTR_STAT=%#x\n", + enabled, slave_activity, raw_stat, stat); + + if ((stat & IC_INTR_RX_FULL) && (stat & IC_INTR_STOP_DET)) + i2c_slave_event(dev->slave, I2C_SLAVE_WRITE_REQUESTED, &val); + + if (stat & IC_INTR_RD_REQ) { + if (slave_activity) { + if (stat & IC_INTR_RX_FULL) { + val = phytium_readl(dev, IC_DATA_CMD); + + if (!i2c_slave_event(dev->slave, + I2C_SLAVE_WRITE_RECEIVED, + &val)) { + dev_vdbg(dev->dev, "Byte %X acked!", + val); + } + phytium_readl(dev, IC_CLR_RD_REQ); + stat = i2c_phytium_read_clear_intrbits_slave(dev); + } else { + phytium_readl(dev, IC_CLR_RD_REQ); + phytium_readl(dev, IC_CLR_RX_UNDER); + stat = i2c_phytium_read_clear_intrbits_slave(dev); + } + if (!i2c_slave_event(dev->slave, + I2C_SLAVE_READ_REQUESTED, + &val)) + phytium_writel(dev, val, IC_DATA_CMD); + } + } + + if (stat & IC_INTR_RX_DONE) { + if (!i2c_slave_event(dev->slave, I2C_SLAVE_READ_PROCESSED, + &val)) + phytium_readl(dev, IC_CLR_RX_DONE); + + i2c_slave_event(dev->slave, I2C_SLAVE_STOP, &val); + stat = i2c_phytium_read_clear_intrbits_slave(dev); + return 1; + } + + if (stat & IC_INTR_RX_FULL) { + val = phytium_readl(dev, IC_DATA_CMD); + if (!i2c_slave_event(dev->slave, I2C_SLAVE_WRITE_RECEIVED, + &val)) + dev_vdbg(dev->dev, "Byte %X acked!", val); + } else { + i2c_slave_event(dev->slave, I2C_SLAVE_STOP, &val); + stat = i2c_phytium_read_clear_intrbits_slave(dev); + } + + return 1; +} + +static irqreturn_t i2c_phytium_isr_slave(int this_irq, void *dev_id) +{ + struct phytium_i2c_dev *dev = dev_id; + int ret; + + i2c_phytium_read_clear_intrbits_slave(dev); + ret = i2c_phytium_irq_handler_slave(dev); + if (ret > 0) + complete(&dev->cmd_complete); + + return IRQ_RETVAL(ret); +} + +static const struct i2c_algorithm i2c_phytium_algo = { + .functionality = i2c_phytium_func, + .reg_slave = i2c_phytium_reg_slave, + .unreg_slave = i2c_phytium_unreg_slave, +}; + +int i2c_phytium_probe_slave(struct phytium_i2c_dev *dev) +{ + struct i2c_adapter *adap = &dev->adapter; + int ret; + + init_completion(&dev->cmd_complete); + + dev->init = i2c_phytium_init_slave; + dev->disable = i2c_phytium_disable; + dev->disable_int = i2c_phytium_disable_int; + + ret = dev->init(dev); + if (ret) + return ret; + + snprintf(adap->name, sizeof(adap->name), + "Phytium I2C Slave adapter"); + adap->retries = 3; + adap->algo = &i2c_phytium_algo; + adap->dev.parent = dev->dev; + i2c_set_adapdata(adap, dev); + + ret = devm_request_irq(dev->dev, dev->irq, i2c_phytium_isr_slave, + IRQF_SHARED, dev_name(dev->dev), dev); + if (ret) { + dev_err(dev->dev, "failure requesting irq %i: %d\n", + dev->irq, ret); + return ret; + } + + ret = i2c_add_numbered_adapter(adap); + if (ret) + dev_err(dev->dev, "failure adding adapter: %d\n", ret); + + return ret; +} +EXPORT_SYMBOL_GPL(i2c_phytium_probe_slave); diff --git a/target/linux/phytium/files-5.10/drivers/iio/adc/phytium-adc.c b/target/linux/phytium/files-5.10/drivers/iio/adc/phytium-adc.c new file mode 100755 index 000000000..b9d8923c9 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/iio/adc/phytium-adc.c @@ -0,0 +1,688 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Phytium ADC device driver + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +/* ADC register */ +#define ADC_CTRL_REG 0x00 +#define ADC_CTRL_REG_PD_EN BIT(31) +#define ADC_CTRL_REG_CH_ONLY_S(x) ((x & 0x7) << 16) +#define ADC_CTRL_REG_CLK_DIV(x) ((x) << 12) +#define ADC_CTRL_REG_CHANNEL_EN(x) BIT((x) + 4) +#define ADC_CTRL_REG_CH_ONLY_EN BIT(3) +#define ADC_CTRL_REG_SINGLE_EN BIT(2) +#define ADC_CTRL_REG_SINGLE_SEL BIT(1) +#define ADC_CTRL_REG_SOC_EN BIT(0) +#define ADC_INTER_REG 0x04 +#define ADC_STATE_REG 0x08 +#define ADC_STATE_REG_B_STA(x) ((x) << 8) +#define ADC_STATE_REG_EOC_STA BIT(7) +#define ADC_STATE_REG_S_STA(x) ((x) << 4) +#define ADC_STATE_REG_SOC_STA BIT(3) +#define ADC_STATE_REG_ERR_STA BIT(2) +#define ADC_STATE_REG_COV_FINISH_STA BIT(1) +#define ADC_STATE_REG_ADCCTL_BUSY_STA BIT(0) +#define ADC_ERRCLR_REG 0x0c +#define ADC_LEVEL_REG(x) (0x10 + ((x) << 2)) +#define ADC_LEVEL_REG_HIGH_LEVEL(x) ((x) << 16) +#define ADC_LEVEL_REG_LOW_LEVEL(x) (x) +#define ADC_INTRMASK_REG 0x30 +#define ADC_INTRMASK_REG_ERR_INTR_MASK BIT(24) +#define ADC_INTRMASK_REG_ULIMIT_OFF(x) BIT(9 + ((x) << 1)) +#define ADC_INTRMASK_REG_DLIMIT_MASK(x) BIT(8 + ((x) << 1)) +#define ADC_INTRMASK_REG_COVFIN_MASK(x) BIT((x)) +#define ADC_INTR_REG 0x34 +#define ADC_INTR_REG_ERR BIT(24) +#define ADC_INTR_REG_ULIMIT(x) BIT(9 + ((x) << 1)) +#define ADC_INTR_REG_DLIMIT(x) BIT(8 + ((x) << 1)) +#define ADC_INTR_REG_LIMIT_MASK GENMASK(23, 8) +#define ADC_INTR_REG_COVFIN(x) BIT((x)) +#define ADC_INTR_REG_COVFIN_MASK GENMASK(7, 0) +#define ADC_COV_RESULT_REG(x) (0x38 + ((x) << 2)) +#define ADC_COV_RESULT_REG_MASK GENMASK(9, 0) +#define ADC_FINISH_CNT_REG(x) (0x58 + ((x) << 2)) +#define ADC_HIS_LIMIT_REG(x) (0x78 + ((x) << 2)) + +#define PHYTIUM_MAX_CHANNELS 8 +#define PHYTIUM_ADC_TIMEOUT usecs_to_jiffies(1000 * 1000) + +static const struct iio_event_spec phytium_adc_event[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, +}; + +struct phytium_adc_data { + const struct iio_chan_spec *channels; + u8 num_channels; +}; + +struct phytium_adc { + struct device *dev; + void __iomem *regs; + struct clk *adc_clk; + + u32 interval; + u16 thresh_high[PHYTIUM_MAX_CHANNELS]; + u16 thresh_low[PHYTIUM_MAX_CHANNELS]; + u16 last_val[PHYTIUM_MAX_CHANNELS]; + const struct phytium_adc_data *data; + u16 *scan_data; + + struct completion completion; + struct mutex lock; +}; + +static ssize_t phytium_adc_show_conv_interval(struct iio_dev *indio_dev, + uintptr_t priv, + struct iio_chan_spec const *ch, + char *buf) +{ + struct phytium_adc *adc = iio_priv(indio_dev); + + return sprintf(buf, "%u\n", adc->interval); +} + +static ssize_t phytium_adc_store_conv_interval(struct iio_dev *indio_dev, + uintptr_t priv, + struct iio_chan_spec const *ch, + const char *buf, size_t len) +{ + struct phytium_adc *adc = iio_priv(indio_dev); + u32 interval; + int ret; + + ret = kstrtou32(buf, 0, &interval); + if (ret < 0) + return ret; + + mutex_lock(&adc->lock); + adc->interval = interval; + mutex_unlock(&adc->lock); + + return len; +} + +static const struct iio_chan_spec_ext_info phytium_adc_ext_info[] = { + { + .name = "conv_interval", + .read = phytium_adc_show_conv_interval, + .write = phytium_adc_store_conv_interval, + }, + { /* sentinel */ } +}; + +static int phytium_adc_parse_properties(struct platform_device *pdev, struct phytium_adc *adc) +{ + struct iio_chan_spec *chan_array; + struct fwnode_handle *fwnode; + struct phytium_adc_data *data; + unsigned int channel; + int num_channels; + int ret, i = 0; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + num_channels = device_get_child_node_count(&pdev->dev); + if (!num_channels) { + dev_err(&pdev->dev, "no channel children\n"); + return -ENODEV; + } + + if (num_channels > PHYTIUM_MAX_CHANNELS) { + dev_err(&pdev->dev, "num of channel children out of range\n"); + return -EINVAL; + } + + chan_array = devm_kcalloc(&pdev->dev, num_channels, sizeof(*chan_array), + GFP_KERNEL); + if (!chan_array) + return -ENOMEM; + + device_for_each_child_node(&pdev->dev, fwnode) { + ret = fwnode_property_read_u32(fwnode, "reg", &channel); + if (ret) + return ret; + + if (channel >= PHYTIUM_MAX_CHANNELS) + return -EINVAL; + + chan_array[i].type = IIO_VOLTAGE; + chan_array[i].indexed = 1; + chan_array[i].channel = channel; + chan_array[i].info_mask_separate = BIT(IIO_CHAN_INFO_RAW); + chan_array[i].event_spec = phytium_adc_event; + chan_array[i].num_event_specs = ARRAY_SIZE(phytium_adc_event); + chan_array[i].scan_index = channel; + chan_array[i].scan_type.sign = 'u'; + chan_array[i].scan_type.realbits = 10; + chan_array[i].scan_type.storagebits = 16; + chan_array[i].scan_type.endianness = IIO_LE; + chan_array[i].ext_info = phytium_adc_ext_info; + i++; + } + + data->num_channels = num_channels; + data->channels = chan_array; + adc->data = data; + + return 0; +} + +static void phytium_adc_start_stop(struct phytium_adc *adc, bool start) +{ + u32 ctrl; + + ctrl = readl(adc->regs + ADC_CTRL_REG); + if (start) + ctrl |= ADC_CTRL_REG_SOC_EN | ADC_CTRL_REG_SINGLE_EN; + else + ctrl &= ~ADC_CTRL_REG_SOC_EN; + /* Start conversion */ + writel(ctrl, adc->regs + ADC_CTRL_REG); +} + +static void phytium_adc_power_setup(struct phytium_adc *adc, bool on) +{ + u32 reg; + + reg = readl(adc->regs + ADC_CTRL_REG); + if (on) + reg &= ~ADC_CTRL_REG_PD_EN; + else + reg |= ADC_CTRL_REG_PD_EN; + writel(reg, adc->regs + ADC_CTRL_REG); +} + +static int phytium_adc_hw_init(struct phytium_adc *adc) +{ + int ret; + u32 reg; + + ret = clk_prepare_enable(adc->adc_clk); + if (ret) + return ret; + + /* + * Setup ctrl register: + * - Power up conversion module + * - Set the division by 4 as default + */ + reg = ADC_CTRL_REG_CLK_DIV(4); + writel(reg, adc->regs + ADC_CTRL_REG); + + /* Set all the interrupt mask, unmask them when necessary. */ + writel(0x1ffffff, adc->regs + ADC_INTRMASK_REG); + + /* Set default conversion interval */ + adc->interval = (clk_get_rate(adc->adc_clk) * 1000) / NSEC_PER_SEC; + + phytium_adc_power_setup(adc, true); + + return 0; +} + +static void phytium_adc_intrmask_setup(struct phytium_adc *adc, unsigned long chan_mask, bool on) +{ + u32 reg; + u16 limit_mask = 0; + int ch; + + for_each_set_bit(ch, &chan_mask, PHYTIUM_MAX_CHANNELS) + limit_mask |= BIT(ch << 1) | BIT((ch << 1) + 1); + + reg = readl(adc->regs + ADC_INTRMASK_REG); + if (on) + reg &= ~(ADC_INTRMASK_REG_ERR_INTR_MASK | + (limit_mask << 8) | chan_mask); + else + reg |= (ADC_INTRMASK_REG_ERR_INTR_MASK | + (limit_mask << 8) | chan_mask); + writel(reg, adc->regs + ADC_INTRMASK_REG); +} + +static void phytium_adc_single_conv_setup(struct phytium_adc *adc, u8 ch) +{ + u32 reg; + + /* + * Setup control register: + * - Single conversion mode selection + * - Single conversion enable + * - Fixed channel conversion + * - Target channel + */ + reg = readl(adc->regs + ADC_CTRL_REG); + + /* Clean ch_only_s bits */ + reg &= ~ADC_CTRL_REG_CH_ONLY_S(7); + + /* Clean channel_en bit */ + reg &= 0xFFF00F; + + reg |= ADC_CTRL_REG_SINGLE_SEL | ADC_CTRL_REG_SINGLE_EN | + ADC_CTRL_REG_CH_ONLY_EN | ADC_CTRL_REG_CH_ONLY_S(ch) | ADC_CTRL_REG_CHANNEL_EN(ch); + writel(reg, adc->regs + ADC_CTRL_REG); +} + +static int phytium_adc_single_conv(struct iio_dev *indio_dev, u8 ch) +{ + struct phytium_adc *adc = iio_priv(indio_dev); + int ret; + + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + mutex_lock(&adc->lock); + + phytium_adc_intrmask_setup(adc, BIT(ch), true); + reinit_completion(&adc->completion); + phytium_adc_single_conv_setup(adc, ch); + phytium_adc_start_stop(adc, true); + + if (!wait_for_completion_timeout(&adc->completion, PHYTIUM_ADC_TIMEOUT)) + ret = -ETIMEDOUT; + + phytium_adc_start_stop(adc, false); + phytium_adc_intrmask_setup(adc, BIT(ch), false); + + mutex_unlock(&adc->lock); + iio_device_release_direct_mode(indio_dev); + + return ret; +} + +static int phytium_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct phytium_adc *adc = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (chan->type != IIO_VOLTAGE) + return -EINVAL; + + ret = phytium_adc_single_conv(indio_dev, chan->channel); + if (ret) + return ret; + *val = adc->last_val[chan->channel]; + + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int phytium_read_thresh(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info iinfo, int *val, int *val2) +{ + struct phytium_adc *adc = iio_priv(indio_dev); + + if (dir == IIO_EV_DIR_FALLING) + *val = adc->thresh_low[chan->channel]; + else + *val = adc->thresh_high[chan->channel]; + + return IIO_VAL_INT; +} + +static int phytium_write_thresh(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info einfo, int val, int val2) +{ + struct phytium_adc *adc = iio_priv(indio_dev); + u32 thresh; + + switch (dir) { + case IIO_EV_DIR_FALLING: + adc->thresh_low[chan->channel] = val; + thresh = readl(adc->regs + ADC_LEVEL_REG(chan->channel)) & 0x3ff0000; + thresh |= ADC_LEVEL_REG_LOW_LEVEL(val); + writel(thresh, adc->regs + ADC_LEVEL_REG(chan->channel)); + break; + case IIO_EV_DIR_RISING: + adc->thresh_high[chan->channel] = val; + thresh = readl(adc->regs + ADC_LEVEL_REG(chan->channel)) & 0xffff; + thresh |= ADC_LEVEL_REG_HIGH_LEVEL(val); + writel(thresh, adc->regs + ADC_LEVEL_REG(chan->channel)); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int phytium_update_scan_mode(struct iio_dev *indio_dev, const unsigned long *mask) +{ + struct phytium_adc *adc = iio_priv(indio_dev); + unsigned int n; + + n = bitmap_weight(mask, indio_dev->masklength); + + kfree(adc->scan_data); + adc->scan_data = kcalloc(n, sizeof(*adc->scan_data), GFP_KERNEL); + if (!adc->scan_data) + return -ENOMEM; + + return 0; +} + +static const u64 phytium_adc_event_codes[] = { + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 0, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 0, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING), + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 1, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 1, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING), + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 2, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 2, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING), + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 3, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 3, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING), + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 4, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 4, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING), + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 5, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 5, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING), + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 6, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 6, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING), + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 7, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 7, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING), +}; + +static irqreturn_t phytium_adc_threaded_irq(int irq, void *data) +{ + struct iio_dev *indio_dev = data; + struct phytium_adc *adc = iio_priv(indio_dev); + s64 timestamp = iio_get_time_ns(indio_dev); + unsigned long status; + int ch; + u32 intr; + + intr = readl(adc->regs + ADC_INTR_REG); + + if (intr & ADC_INTR_REG_ERR) { + dev_err(adc->dev, "conversion error: ADC_INTR_REG(0x%x)\n", intr); + writel(ADC_INTR_REG_ERR, adc->regs + ADC_INTR_REG); + return IRQ_HANDLED; + } + + status = (intr & ADC_INTR_REG_LIMIT_MASK) >> 8; + if (status) { + for_each_set_bit(ch, &status, PHYTIUM_MAX_CHANNELS * 2) + iio_push_event(indio_dev, phytium_adc_event_codes[ch], timestamp); + } + + status = intr & ADC_INTR_REG_COVFIN_MASK; + if (status) { + for_each_set_bit(ch, &status, PHYTIUM_MAX_CHANNELS) + adc->last_val[ch] = readl(adc->regs + ADC_COV_RESULT_REG(ch)) & + ADC_COV_RESULT_REG_MASK; + + if (iio_buffer_enabled(indio_dev)) + iio_trigger_poll(indio_dev->trig); + else + complete(&adc->completion); + } + + /* Clear all the interrupts */ + writel(status, adc->regs + ADC_INTR_REG); + + return IRQ_HANDLED; +} + +static void phytium_adc_cont_conv_setup(struct phytium_adc *adc, + unsigned long chan_mask, + u32 interval) +{ + u32 reg; + + /* + * Setup control register: + * - Continuous conversion mode + * - Multi-channel rotation mode + * - Channel enablement + */ + reg = readl(adc->regs + ADC_CTRL_REG); + reg &= ~(ADC_CTRL_REG_SINGLE_SEL | ADC_CTRL_REG_SINGLE_EN | + ADC_CTRL_REG_CH_ONLY_EN); + reg |= chan_mask << 4; + writel(reg, adc->regs + ADC_CTRL_REG); + + /* Setup interval between two conversions */ + writel(interval, adc->regs + ADC_INTER_REG); +} + +static int phytium_adc_preenable(struct iio_dev *indio_dev) +{ + struct phytium_adc *adc = iio_priv(indio_dev); + unsigned long scan_mask = *indio_dev->active_scan_mask; + + phytium_adc_cont_conv_setup(adc, scan_mask & 0xff, adc->interval); + phytium_adc_intrmask_setup(adc, scan_mask & 0xff, true); + + return 0; +} + +static int phytium_adc_postenable(struct iio_dev *indio_dev) +{ + struct phytium_adc *adc = iio_priv(indio_dev); + + phytium_adc_start_stop(adc, true); + + return 0; +} + +static int phytium_adc_postdisable(struct iio_dev *indio_dev) +{ + struct phytium_adc *adc = iio_priv(indio_dev); + unsigned long scan_mask = *indio_dev->active_scan_mask; + + phytium_adc_start_stop(adc, false); + phytium_adc_intrmask_setup(adc, scan_mask & 0xff, false); + + return 0; +} + +static const struct iio_buffer_setup_ops phytium_buffer_setup_ops = { + .preenable = &phytium_adc_preenable, + .postenable = &phytium_adc_postenable, + .postdisable = &phytium_adc_postdisable, +}; + +static irqreturn_t phytium_adc_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct phytium_adc *adc = iio_priv(indio_dev); + int i, j = 0; + + if (!adc->scan_data) + goto out; + + for_each_set_bit(i, indio_dev->active_scan_mask, indio_dev->masklength) + adc->scan_data[j++] = adc->last_val[i]; + + iio_push_to_buffers(indio_dev, adc->scan_data); + +out: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static const struct iio_info phytium_adc_iio_info = { + .read_raw = &phytium_adc_read_raw, + .read_event_value = &phytium_read_thresh, + .write_event_value = &phytium_write_thresh, + .update_scan_mode = &phytium_update_scan_mode, +}; + +static int phytium_adc_probe(struct platform_device *pdev) +{ + struct phytium_adc *adc; + struct iio_dev *indio_dev; + struct device *dev = &pdev->dev; + struct resource *res; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*adc)); + if (!indio_dev) + return -ENOMEM; + + adc = iio_priv(indio_dev); + adc->dev = dev; + + ret = phytium_adc_parse_properties(pdev, adc); + if (ret) + return ret; + + mutex_init(&adc->lock); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + adc->regs = devm_ioremap_resource(dev, res); + if (IS_ERR(adc->regs)) + return PTR_ERR(adc->regs); + + adc->adc_clk = devm_clk_get(dev, NULL); + if (IS_ERR(adc->adc_clk)) + return PTR_ERR(adc->adc_clk); + + init_completion(&adc->completion); + + indio_dev->name = dev_name(dev); + indio_dev->info = &phytium_adc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = adc->data->channels; + indio_dev->num_channels = adc->data->num_channels; + + platform_set_drvdata(pdev, indio_dev); + + ret = devm_request_threaded_irq(adc->dev, platform_get_irq(pdev, 0), + NULL, phytium_adc_threaded_irq, IRQF_ONESHOT, + dev_name(dev), indio_dev); + if (ret) + return ret; + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, + &iio_pollfunc_store_time, + phytium_adc_trigger_handler, + &phytium_buffer_setup_ops); + if (ret) + return ret; + + ret = phytium_adc_hw_init(adc); + if (ret) { + dev_err(&pdev->dev, "failed to initialize Phytium ADC, %d\n", ret); + return ret; + } + + return iio_device_register(indio_dev); +} + +static int phytium_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct phytium_adc *adc = iio_priv(indio_dev); + + phytium_adc_power_setup(adc, false); + iio_device_unregister(indio_dev); + kfree(adc->scan_data); + + return 0; +} + +static const struct of_device_id phytium_of_match[] = { + { .compatible = "phytium,adc", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, phytium_of_match); + +#ifdef CONFIG_PM +static int phytium_adc_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct phytium_adc *adc = iio_priv(indio_dev); + + phytium_adc_power_setup(adc, false); + clk_disable_unprepare(adc->adc_clk); + + return 0; +} + +static int phytium_adc_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct phytium_adc *adc = iio_priv(indio_dev); + + clk_prepare_enable(adc->adc_clk); + phytium_adc_power_setup(adc, true); + + return 0; +} +#endif + +SIMPLE_DEV_PM_OPS(phytium_adc_pm_ops, phytium_adc_suspend, phytium_adc_resume); + +static struct platform_driver phytium_adc_driver = { + .driver = { + .name = "phytium_adc", + .of_match_table = phytium_of_match, + .pm = &phytium_adc_pm_ops, + }, + .probe = phytium_adc_probe, + .remove = phytium_adc_remove, +}; +module_platform_driver(phytium_adc_driver); + +MODULE_AUTHOR("Yang Liu "); +MODULE_DESCRIPTION("Phytium ADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/target/linux/phytium/files-5.10/drivers/input/keyboard/phytium-keypad.c b/target/linux/phytium/files-5.10/drivers/input/keyboard/phytium-keypad.c new file mode 100644 index 000000000..e5ae4a27c --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/input/keyboard/phytium-keypad.c @@ -0,0 +1,586 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for the Phytium keypad port. + * + * Copyright (c) 2020-2023 Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Keypad Controller registers + */ +#define KPCR 0x00 /* Keypad Control Register */ + +#define KPSR 0x04 /* Keypad Status Register */ +#define KBD_STAT_KPKD (0x1 << 0) /* Key Press Interrupt Status bit (w1c) */ +#define KBD_STAT_KPKR (0x1 << 1) /* Key Release Interrupt Status bit (w1c) */ +#define KBD_STAT_KDSC (0x1 << 2) /* Key Depress Synch Chain Status bit (w1c)*/ +#define KBD_STAT_KRSS (0x1 << 3) /* Key Release Synch Status bit (w1c)*/ +#define KBD_STAT_KDIE (0x1 << 8) /* Key Depress Interrupt Enable Status bit */ +#define KBD_STAT_KRIE (0x1 << 9) /* Key Release Interrupt Enable */ + +#define KDDR 0x08 /* Keypad Data Direction Register */ +#define KPDR 0x0C /* Keypad Data Register */ + +#define MAX_MATRIX_KEY_ROWS 8 +#define MAX_MATRIX_KEY_COLS 8 + +#define MAX_MATRIX_KEY_NUM (MAX_MATRIX_KEY_ROWS * MAX_MATRIX_KEY_COLS) + +struct phytium_keypad { + struct input_dev *input_dev; + void __iomem *mmio_base; + + int irq; + struct timer_list check_matrix_timer; + + /* + * The matrix is stable only if no changes are detected after + * PHYTIUM_KEYPAD_SCANS_FOR_STABILITY scans + */ +#define PHYTIUM_KEYPAD_SCANS_FOR_STABILITY 3 + + int stable_count; + + bool enabled; + + unsigned int n_rows; + unsigned int n_cols; + int row_shift; + + /* Masks for enabled rows/cols */ + unsigned short rows_en_mask; + unsigned short cols_en_mask; + + unsigned short keycodes[MAX_MATRIX_KEY_NUM]; + + /* + * Matrix states: + * -stable: achieved after a complete debounce process. + * -unstable: used in the debouncing process. + */ + unsigned short matrix_stable_state[MAX_MATRIX_KEY_COLS]; + unsigned short matrix_unstable_state[MAX_MATRIX_KEY_COLS]; +}; + +static u32 phytium_read(struct phytium_keypad *keypad, int reg) +{ + return readl(keypad->mmio_base + reg); +} + +static void phytium_write(struct phytium_keypad *keypad, u32 value, int reg) +{ + writel(value, keypad->mmio_base + reg); +} + +/* Scan the matrix and return the new state in *matrix_volatile_state. */ +static void phytium_keypad_scan_matrix(struct phytium_keypad *keypad, + unsigned short *matrix_volatile_state) +{ + int col; + u32 reg_val; + + for (col = 0; col < keypad->n_cols; col++) { + if ((keypad->cols_en_mask & (1 << col)) == 0) + continue; + /* + * Discharge keypad capacitance: + * 2. write 0s on KDDR[KCD], configure columns as input. + */ + reg_val = phytium_read(keypad, KDDR); + reg_val = 0x00000000; + phytium_write(keypad, reg_val, KDDR); + + /* + * 3. Write a single column to 0, others to 1. + * 4. Sample row inputs and save data. + * 5. Repeat steps 3 - 4 for remaining columns. + */ + reg_val = 0; + reg_val |= (1 << (16 + col)); + phytium_write(keypad, reg_val, KDDR); + reg_val = phytium_read(keypad, KPDR); + reg_val = 0x00000000; + phytium_write(keypad, reg_val, KPDR); + + /* + * Delay added to avoid propagating the 0 from column to row + * when scanning. + */ + udelay(5); + + /* + * 1s in matrix_volatile_state[col] means key pressures + * throw data from non enabled rows. + */ + reg_val = phytium_read(keypad, KPDR); + matrix_volatile_state[col] = (~reg_val) & keypad->rows_en_mask; + } + + /* + * Return in standby mode: + * 6. write 0s to columns + */ + /* Configure columns as output, output 0 */ + reg_val = 0; + reg_val |= (keypad->cols_en_mask & 0xffff) << 16; + phytium_write(keypad, reg_val, KDDR); + phytium_write(keypad, 0x00000000, KPDR); +} + +/* + * Compare the new matrix state (volatile) with the stable one stored in + * keypad->matrix_stable_state and fire events if changes are detected. + */ +static void phytium_keypad_fire_events(struct phytium_keypad *keypad, + unsigned short *matrix_volatile_state) +{ + struct input_dev *input_dev = keypad->input_dev; + int row, col; + + for (col = 0; col < keypad->n_cols; col++) { + unsigned short bits_changed; + int code; + + if ((keypad->cols_en_mask & (1 << col)) == 0) + continue; /* Column is not enabled */ + + bits_changed = keypad->matrix_stable_state[col] ^ + matrix_volatile_state[col]; + + if (bits_changed == 0) + continue; /* Column does not contain changes */ + + for (row = 0; row < keypad->n_rows; row++) { + if ((keypad->rows_en_mask & (1 << row)) == 0) + continue; /* Row is not enabled */ + if ((bits_changed & (1 << row)) == 0) + continue; /* Row does not contain changes */ + + code = MATRIX_SCAN_CODE(row, col, keypad->row_shift); + input_event(input_dev, EV_MSC, MSC_SCAN, code); + input_report_key(input_dev, keypad->keycodes[code], + matrix_volatile_state[col] & (1 << row)); + dev_dbg(&input_dev->dev, "Event code: %d, val: %d", + keypad->keycodes[code], + matrix_volatile_state[col] & (1 << row)); + } + } + input_sync(input_dev); +} + +/* + * phytium_keypad_check_for_events is the timer handler. + */ +static void phytium_keypad_check_for_events(struct timer_list *t) +{ + struct phytium_keypad *keypad = from_timer(keypad, t, check_matrix_timer); + unsigned short matrix_volatile_state[MAX_MATRIX_KEY_COLS]; + u32 reg_val; + bool state_changed, is_zero_matrix; + int i; + + memset(matrix_volatile_state, 0, sizeof(matrix_volatile_state)); + + phytium_keypad_scan_matrix(keypad, matrix_volatile_state); + + state_changed = false; + for (i = 0; i < keypad->n_cols; i++) { + if ((keypad->cols_en_mask & (1 << i)) == 0) + continue; + + if (keypad->matrix_unstable_state[i] ^ matrix_volatile_state[i]) { + state_changed = true; + break; + } + } + + /* + * If the matrix state is changed from the previous scan + * (Re)Begin the debouncing process, saving the new state in + * keypad->matrix_unstable_state. + * else + * Increase the count of number of scans with a stable state. + */ + if (state_changed) { + memcpy(keypad->matrix_unstable_state, matrix_volatile_state, + sizeof(matrix_volatile_state)); + keypad->stable_count = 0; + } else { + keypad->stable_count++; + } + + /* + * If the matrix is not as stable as we want reschedule scan + * in the near future. + */ + if (keypad->stable_count < PHYTIUM_KEYPAD_SCANS_FOR_STABILITY) { + mod_timer(&keypad->check_matrix_timer, + jiffies + msecs_to_jiffies(10)); + return; + } + + /* + * If the matrix state is stable, fire the events and save the new + * stable state. Note, if the matrix is kept stable for longer + * (keypad->stable_count > PHYTIUM_KEYPAD_SCANS_FOR_STABILITY) all + * events have already been generated. + */ + if (keypad->stable_count == PHYTIUM_KEYPAD_SCANS_FOR_STABILITY) { + phytium_keypad_fire_events(keypad, matrix_volatile_state); + memcpy(keypad->matrix_stable_state, matrix_volatile_state, + sizeof(matrix_volatile_state)); + } + + is_zero_matrix = true; + for (i = 0; i < keypad->n_cols; i++) { + if (matrix_volatile_state[i] != 0) { + is_zero_matrix = false; + break; + } + } + + if (is_zero_matrix) { + /* + * All keys have been released. Enable only the KDI + * interrupt for future key presses (clear the KDI + * status bit and its sync chain before that). + */ + reg_val = phytium_read(keypad, KPSR); + reg_val |= KBD_STAT_KPKD | KBD_STAT_KDSC; + phytium_write(keypad, reg_val, KPSR); + + reg_val = phytium_read(keypad, KPSR); + reg_val |= KBD_STAT_KDIE; + reg_val &= ~KBD_STAT_KRIE; + phytium_write(keypad, reg_val, KPSR); + } else { + /* + * Some keys are still pressed. Schedule a rescan in + * attempt to detect multiple key presses and enable + * the KRI interrupt to react quickly to key release + * event. + */ + mod_timer(&keypad->check_matrix_timer, + jiffies + msecs_to_jiffies(60)); + + reg_val = phytium_read(keypad, KPSR); + reg_val |= KBD_STAT_KPKR | KBD_STAT_KRSS; + phytium_write(keypad, reg_val, KPSR); + + reg_val = phytium_read(keypad, KPSR); + reg_val |= KBD_STAT_KRIE; + reg_val &= ~KBD_STAT_KDIE; + phytium_write(keypad, reg_val, KPSR); + } +} + +static irqreturn_t phytium_keypad_irq_handler(int irq, void *dev_id) +{ + struct phytium_keypad *keypad = dev_id; + u32 reg_val; + + reg_val = phytium_read(keypad, KPSR); + /* Disable both interrupt types */ + reg_val &= ~(KBD_STAT_KRIE | KBD_STAT_KDIE); + /* Clear interrupts status bits */ + reg_val |= KBD_STAT_KPKR | KBD_STAT_KPKD; + phytium_write(keypad, reg_val, KPSR); + + if (keypad->enabled) { + /* The matrix is supposed to be changed */ + keypad->stable_count = 0; + + /* Schedule the scanning procedure near in the future */ + mod_timer(&keypad->check_matrix_timer, + jiffies + msecs_to_jiffies(2)); + } + + return IRQ_HANDLED; +} + +static void phytium_keypad_config(struct phytium_keypad *keypad) +{ + u32 reg_val; + + /* + * Include enabled rows in interrupt generation (KPCR[15:0]) + * Configure keypad columns as open-drain (KPCR[31:16]) + */ + reg_val = phytium_read(keypad, KPCR); + reg_val |= keypad->rows_en_mask & 0xffff; /* rows */ + reg_val |= (keypad->cols_en_mask & 0xffff) << 16; /* cols */ + phytium_write(keypad, reg_val, KPCR); + + /* Configure columns as output, output 0 */ + reg_val = 0; + reg_val |= (keypad->cols_en_mask & 0xffff) << 16; + phytium_write(keypad, reg_val, KDDR); + phytium_write(keypad, 0x00000000, KPDR); + + /* + * Clear Key Depress and Key Release status bit. + * Clear both synchronizer chain. + */ + reg_val = phytium_read(keypad, KPSR); + reg_val |= KBD_STAT_KPKR | KBD_STAT_KPKD | + KBD_STAT_KDSC | KBD_STAT_KRSS; + phytium_write(keypad, reg_val, KPSR); + + /* Enable KDI and disable KRI (avoid false release events). */ + reg_val |= KBD_STAT_KDIE; + reg_val &= ~KBD_STAT_KRIE; + phytium_write(keypad, reg_val, KPSR); +} + +static void phytium_keypad_inhibit(struct phytium_keypad *keypad) +{ + unsigned short reg_val; + + /* Inhibit KDI and KRI interrupts. */ + reg_val = phytium_read(keypad, KPSR); + reg_val &= ~(KBD_STAT_KRIE | KBD_STAT_KDIE); + reg_val |= KBD_STAT_KPKR | KBD_STAT_KPKD; + phytium_write(keypad, reg_val, KPSR); +} + +static void phytium_keypad_close(struct input_dev *dev) +{ + struct phytium_keypad *keypad = input_get_drvdata(dev); + + dev_dbg(&dev->dev, ">%s\n", __func__); + + /* Mark keypad as being inactive */ + keypad->enabled = false; + synchronize_irq(keypad->irq); + del_timer_sync(&keypad->check_matrix_timer); + + phytium_keypad_inhibit(keypad); +} + +static int phytium_keypad_open(struct input_dev *dev) +{ + struct phytium_keypad *keypad = input_get_drvdata(dev); + + dev_dbg(&dev->dev, ">%s\n", __func__); + + /* We became active from now */ + keypad->enabled = true; + + phytium_keypad_config(keypad); + + /* Sanity control, not all the rows must be activated now. */ + if ((phytium_read(keypad, KPDR) & keypad->rows_en_mask) == 0) { + dev_err(&dev->dev, + "too many keys pressed, control pins initialisation\n"); + goto open_err; + } + + return 0; + +open_err: + phytium_keypad_close(dev); + return -EIO; +} + +#ifdef CONFIG_ACPI +static const struct acpi_device_id phytium_keypad_acpi_ids[] = { + { "PHYT0028", 0 }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(acpi, phytium_keypad_acpi_ids); +#endif + +#ifdef CONFIG_OF +static const struct of_device_id phytium_keypad_of_match[] = { + { .compatible = "phytium,keypad", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, phytium_keypad_of_match); +#endif + +static int phytium_keypad_probe(struct platform_device *pdev) +{ + const struct matrix_keymap_data *keymap_data = dev_get_platdata(&pdev->dev); + struct phytium_keypad *keypad; + struct input_dev *input_dev; + struct resource *res; + int irq, error, i, row, col; + + if (!keymap_data && !pdev->dev.of_node && !has_acpi_companion(&pdev->dev)) { + dev_err(&pdev->dev, "no keymap defined\n"); + return -EINVAL; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "no irq defined in platform data\n"); + return irq; + } + + input_dev = devm_input_allocate_device(&pdev->dev); + if (!input_dev) { + dev_err(&pdev->dev, "failed to allocate the input device\n"); + return -ENOMEM; + } + + keypad = devm_kzalloc(&pdev->dev, sizeof(*keypad), GFP_KERNEL); + if (!keypad) + return -ENOMEM; + + keypad->input_dev = input_dev; + keypad->irq = irq; + keypad->stable_count = 0; + + timer_setup(&keypad->check_matrix_timer, + phytium_keypad_check_for_events, 0); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + keypad->mmio_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(keypad->mmio_base)) + return PTR_ERR(keypad->mmio_base); + + /* Init the Input device */ + input_dev->name = pdev->name; + input_dev->id.bustype = BUS_HOST; + input_dev->dev.parent = &pdev->dev; + input_dev->open = phytium_keypad_open; + input_dev->close = phytium_keypad_close; + + error = matrix_keypad_parse_properties(&pdev->dev, &keypad->n_rows, &keypad->n_cols); + if (error) { + dev_err(&pdev->dev, "failed to parse phytium kp params\n"); + return error; + } + + error = matrix_keypad_build_keymap(keymap_data, NULL, + keypad->n_rows, + keypad->n_cols, + keypad->keycodes, input_dev); + if (error) { + dev_err(&pdev->dev, "failed to build keymap\n"); + return error; + } + + keypad->row_shift = get_count_order(keypad->n_cols); + + /* Search for rows and cols enabled */ + for (row = 0; row < keypad->n_rows; row++) { + for (col = 0; col < keypad->n_cols; col++) { + i = MATRIX_SCAN_CODE(row, col, keypad->row_shift); + if (keypad->keycodes[i] != KEY_RESERVED) { + keypad->rows_en_mask |= 1 << row; + keypad->cols_en_mask |= 1 << col; + } + } + } + + __set_bit(EV_REP, input_dev->evbit); + input_set_capability(input_dev, EV_MSC, MSC_SCAN); + input_set_drvdata(input_dev, keypad); + + phytium_keypad_inhibit(keypad); + + error = devm_request_irq(&pdev->dev, irq, phytium_keypad_irq_handler, 0, + pdev->name, keypad); + if (error) { + dev_err(&pdev->dev, "failed to request IRQ\n"); + return error; + } + + /* Register the input device */ + error = input_register_device(input_dev); + if (error) { + dev_err(&pdev->dev, "failed to register input device\n"); + return error; + } + + platform_set_drvdata(pdev, keypad); + device_init_wakeup(&pdev->dev, 1); + + return 0; +} + +static int phytium_keypad_remove(struct platform_device *pdev) +{ + struct phytium_keypad *keypad = platform_get_drvdata(pdev); + + input_unregister_device(keypad->input_dev); + devm_kfree(&pdev->dev, keypad); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int phytium_keypad_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct phytium_keypad *keypad = platform_get_drvdata(pdev); + struct input_dev *input_dev = keypad->input_dev; + + mutex_lock(&input_dev->mutex); + + if (input_dev->users) + phytium_keypad_inhibit(keypad); + + mutex_unlock(&input_dev->mutex); + + if (device_may_wakeup(&pdev->dev)) + enable_irq_wake(keypad->irq); + + return 0; +} + +static int phytium_keypad_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct phytium_keypad *keypad = platform_get_drvdata(pdev); + struct input_dev *input_dev = keypad->input_dev; + int ret = 0; + + if (device_may_wakeup(&pdev->dev)) + disable_irq_wake(keypad->irq); + + mutex_lock(&input_dev->mutex); + + if (input_dev->users) + phytium_keypad_config(keypad); + + mutex_unlock(&input_dev->mutex); + + return ret; +} +#endif + +static SIMPLE_DEV_PM_OPS(phytium_keypad_pm_ops, phytium_keypad_suspend, phytium_keypad_resume); + +static struct platform_driver phytium_keypad_driver = { + .driver = { + .name = "phytium-keypad", + .pm = &phytium_keypad_pm_ops, + .of_match_table = of_match_ptr(phytium_keypad_of_match), + .acpi_match_table = ACPI_PTR(phytium_keypad_acpi_ids), + }, + .probe = phytium_keypad_probe, + .remove = phytium_keypad_remove, +}; +module_platform_driver(phytium_keypad_driver); + +MODULE_AUTHOR("Song Wenting "); +MODULE_DESCRIPTION("PHYTIUM Keypad Port Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:phytium-keypad"); diff --git a/target/linux/phytium/files-5.10/drivers/input/serio/phytium-ps2.c b/target/linux/phytium/files-5.10/drivers/input/serio/phytium-ps2.c new file mode 100644 index 000000000..d08433d95 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/input/serio/phytium-ps2.c @@ -0,0 +1,186 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Phytium PS/2 keyboard controller driver. + * + * Copyright (C) 2021-2023, Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "phytium_ps2_pci" + +#define REG_STAT 0x0 +#define REG_STAT_TX_TIMEOUT 0x1 +#define REG_STAT_RX_TIMEOUT 0x2 +#define REG_STAT_TX_FULL 0x4 +#define REG_CTRL 0x4 +#define REG_CTRL_RESET 0x1 +#define REG_CTRL_TX_TIMEOUT 0x2 +#define REG_CTRL_RX_TIMEOUT 0x4 +#define REG_CTRL_RX_INTR 0x8 +#define REG_INTR 0x8 +#define REG_INTR_TIMEOUT 0x1 +#define REG_INTR_RX 0x2 +#define REG_TX 0xc +#define REG_RX 0x10 +#define REG_TIMER_VAL 0x14 + +#define REG_CTRL_ENABLE (REG_CTRL_TX_TIMEOUT|REG_CTRL_RX_TIMEOUT|REG_CTRL_RX_INTR) +#define REG_DATA_PARITY 0x100 + +#define STAT_RX_COUNTER(stat) ((stat >> 8) & 0x1f) + +struct phytium_ps2_data { + void __iomem *base; + struct serio *io; + struct pci_dev *dev; +}; + +static irqreturn_t phytium_ps2_irq(int irq, void *devid) +{ + struct phytium_ps2_data *ps2if = devid; + u32 status, scancode, val = 0; + unsigned int flag; + int i, rxcount; + + status = readl(ps2if->base + REG_STAT); + if (!status) + return IRQ_NONE; + + /* Check if there is timeout interrupt */ + if (status & (REG_STAT_RX_TIMEOUT|REG_STAT_TX_TIMEOUT)) + val |= REG_INTR_TIMEOUT; + + rxcount = STAT_RX_COUNTER(status); + for (i = 0; i < rxcount; i++) { + scancode = readl(ps2if->base + REG_RX) & 0x1ff; + + if (rxcount <= 16 && scancode != 0x1ff) { + flag = ((scancode & REG_DATA_PARITY) ? SERIO_PARITY : 0); + serio_interrupt(ps2if->io, scancode & 0xff, flag); + } + } + + val |= REG_INTR_RX; + writel(val, ps2if->base + REG_INTR); + + return IRQ_HANDLED; +} + +int phytium_ps2_write(struct serio *serio, unsigned char val) +{ + struct phytium_ps2_data *ps2if = serio->port_data; + unsigned int stat; + + do { + stat = readl(ps2if->base + REG_STAT); + cpu_relax(); + } while (stat & REG_STAT_TX_FULL); + + writel(val, ps2if->base + REG_TX); + + return 0; +} + +int phytium_ps2_open(struct serio *io) +{ + struct phytium_ps2_data *ps2if = io->port_data; + + writel(REG_CTRL_RESET, ps2if->base + REG_CTRL); + /* Wait 4ms for the controller to be reset */ + usleep_range(4000, 6000); + writel(REG_CTRL_ENABLE, ps2if->base + REG_CTRL); + + return 0; +} + +void phytium_ps2_close(struct serio *io) +{ + struct phytium_ps2_data *ps2if = io->port_data; + + writel(0, ps2if->base + REG_CTRL); +} + +static int phytium_pci_ps2_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct phytium_ps2_data *ps2if; + struct serio *serio; + int ret; + + ret = pcim_enable_device(pdev); + if (ret) + goto out; + + ret = pcim_iomap_regions(pdev, 0x1, DRV_NAME); + if (ret) + goto out; + + ps2if = devm_kzalloc(&pdev->dev, sizeof(struct phytium_ps2_data), GFP_KERNEL); + serio = kzalloc(sizeof(struct serio), GFP_KERNEL); + if (!ps2if || !serio) { + ret = -ENOMEM; + goto free; + } + + serio->id.type = SERIO_8042; + serio->write = phytium_ps2_write; + serio->open = phytium_ps2_open; + serio->close = phytium_ps2_close; + strscpy(serio->name, pci_name(pdev), sizeof(serio->name)); + strscpy(serio->phys, dev_name(&pdev->dev), sizeof(serio->phys)); + serio->port_data = ps2if; + serio->dev.parent = &pdev->dev; + ps2if->io = serio; + ps2if->dev = pdev; + ps2if->base = pcim_iomap_table(pdev)[0]; + + ret = devm_request_irq(&pdev->dev, pdev->irq, phytium_ps2_irq, + IRQF_SHARED, DRV_NAME, ps2if); + if (ret) { + dev_err(&pdev->dev, "could not request IRQ %d\n", pdev->irq); + goto free; + } + + pci_set_drvdata(pdev, ps2if); + serio_register_port(ps2if->io); + + return 0; + +free: + kfree(serio); +out: + return ret; +} + +static void phytium_pci_ps2_remove(struct pci_dev *pdev) +{ + struct phytium_ps2_data *ps2if = pci_get_drvdata(pdev); + + serio_unregister_port(ps2if->io); + pcim_iounmap_regions(pdev, 0x1); +} + +static const struct pci_device_id phytium_pci_ps2_ids[] = { + { PCI_VDEVICE(PHYTIUM, 0xdc34) }, + {}, +}; +MODULE_DEVICE_TABLE(pci, phytium_pci_ps2_ids); + +static struct pci_driver phytium_pci_ps2_driver = { + .name = DRV_NAME, + .id_table = phytium_pci_ps2_ids, + .probe = phytium_pci_ps2_probe, + .remove = phytium_pci_ps2_remove, +}; +module_pci_driver(phytium_pci_ps2_driver); + +MODULE_AUTHOR("Cheng Quan "); +MODULE_DESCRIPTION("Phytium PCI PS/2 controller driver"); +MODULE_LICENSE("GPL"); diff --git a/target/linux/phytium/files-5.10/drivers/irqchip/irq-phytium-ixic.c b/target/linux/phytium/files-5.10/drivers/irqchip/irq-phytium-ixic.c new file mode 100755 index 000000000..7862df80f --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/irqchip/irq-phytium-ixic.c @@ -0,0 +1,264 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for Phytium PCIe legacy INTx interrupt controller + * + * Copyright (c) 2020-2023, Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define NUM_IRQS 4 + +#define CTR_BANK_NUM 6 +#define CTR_BANK_SIZE 0x10000 +#define CTR_BANK_ISTATUS_LOCAL 0x184 + +#define HPB_INTX_STATUS_0 0x0 +#define HPB_INTX_STATUS_1 0x1000 + +struct ixic_irq_data { + void __iomem *ctr; + void __iomem *hpb; + u32 spi_base; +}; + +static void phytium_ixic_irq_eoi(struct irq_data *d) +{ + struct ixic_irq_data *data = irq_data_get_irq_chip_data(d); + unsigned int intx = irqd_to_hwirq(d); + u32 gstatus = readl(data->hpb) | (readl(data->hpb + HPB_INTX_STATUS_1) << 12); + u32 imask, istatus; + int i; + + WARN_ON(intx >= NUM_IRQS); + imask = 1 << (3 - intx); + istatus = (1 << intx) << 24; + for (i = 0; i < CTR_BANK_NUM; i++, gstatus >>= 4) { + if (gstatus & imask) + writel(istatus, data->ctr + CTR_BANK_SIZE*i + CTR_BANK_ISTATUS_LOCAL); + } + + irq_chip_eoi_parent(d); +} + +static struct irq_chip phytium_ixic_irq_chip = { + .name = "IXIU", + .irq_eoi = phytium_ixic_irq_eoi, + .irq_mask = irq_chip_mask_parent, + .irq_unmask = irq_chip_unmask_parent, + .irq_set_type = irq_chip_set_type_parent, + .irq_set_affinity = irq_chip_set_affinity_parent, + .flags = IRQCHIP_MASK_ON_SUSPEND, +}; + +static int phytium_ixic_translate(struct irq_domain *domain, + struct irq_fwspec *fwspec, + unsigned long *hwirq, + unsigned int *type) +{ + struct ixic_irq_data *info = domain->host_data; + + if (is_of_node(fwspec->fwnode)) { + if (fwspec->param_count != 3) + return -EINVAL; + + if (fwspec->param[0] != GIC_SPI) + return -EINVAL; /* No PPI should point to this domain */ + + *hwirq = fwspec->param[1] - info->spi_base; + *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK; + } else { + if (fwspec->param_count != 2) + return -EINVAL; + *hwirq = fwspec->param[0] - info->spi_base; + *type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK; + } + + return 0; +} + +static int phytium_ixic_alloc(struct irq_domain *dom, unsigned int virq, + unsigned int nr_irqs, void *data) +{ + struct irq_fwspec *fwspec = data; + struct irq_fwspec parent_fwspec; + struct ixic_irq_data *info = dom->host_data; + irq_hw_number_t hwirq; + + /* We assume the device use the parent's format directly */ + parent_fwspec = *fwspec; + if (is_of_node(dom->parent->fwnode)) { + if (fwspec->param_count != 3) + return -EINVAL; /* Not GIC compliant */ + if (fwspec->param[0] != GIC_SPI) + return -EINVAL; /* No PPI should point to this domain */ + + /* Get the local hwirq of IXIC */ + hwirq = fwspec->param[1] - info->spi_base; + } else { + hwirq = fwspec->param[0] - info->spi_base; + } + WARN_ON(nr_irqs != 1); + irq_domain_set_hwirq_and_chip(dom, virq, hwirq, &phytium_ixic_irq_chip, info); + + parent_fwspec.fwnode = dom->parent->fwnode; + return irq_domain_alloc_irqs_parent(dom, virq, nr_irqs, &parent_fwspec); +} + +static const struct irq_domain_ops ixic_domain_ops = { + .translate = phytium_ixic_translate, + .alloc = phytium_ixic_alloc, + .free = irq_domain_free_irqs_common, +}; + +static struct ixic_irq_data *phytium_ixic_init(const struct fwnode_handle *fwnode, + struct resource *ctr, struct resource *hpb) +{ + struct ixic_irq_data *data; + int err; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return ERR_PTR(-ENOMEM); + + if (fwnode_property_read_u32_array(fwnode, "intx-spi-base", + &data->spi_base, 1)) { + err = -ENODEV; + goto out_free; + } + + data->ctr = ioremap(ctr->start, resource_size(ctr)); + if (!data->ctr) { + err = -ENODEV; + goto out_free; + } + + data->hpb = ioremap(hpb->start, resource_size(hpb)); + if (!data->hpb) { + err = -ENODEV; + goto out_free; + } + + return data; + +out_free: + kfree(data); + return ERR_PTR(err); +} + +static int __init phytium_ixic_dt_init(struct device_node *node, + struct device_node *parent) +{ + struct irq_domain *pd, *d; + struct ixic_irq_data *data; + struct resource ctr, hpb; + + if (!parent) { + pr_err("%pOF: no parent, giving up\n", node); + return -ENODEV; + } + + pd = irq_find_host(parent); + if (!pd) { + pr_err("%pOF: unable to obtain parent domain\n", node); + return -ENXIO; + } + + if (of_address_to_resource(node, 0, &ctr)) { + pr_err("%pOF: failed to parse 'ctr' memory resource\n", node); + return -ENXIO; + } + + if (of_address_to_resource(node, 1, &hpb)) { + pr_err("%pOF: failed to parse 'hpb' memory resource\n", node); + return -ENXIO; + } + + data = phytium_ixic_init(of_node_to_fwnode(node), &ctr, &hpb); + if (IS_ERR(data)) + return PTR_ERR(data); + + d = irq_domain_add_hierarchy(pd, 0, NUM_IRQS, node, &ixic_domain_ops, data); + if (!d) { + pr_err("%pOF: failed to allocate domain\n", node); + goto out_unmap; + } + + pr_info("%pOF: %d interrupts forwarded to %pOF\n", node, NUM_IRQS, parent); + + return 0; + +out_unmap: + iounmap(data->ctr); + iounmap(data->hpb); + kfree(data); + return -ENOMEM; +} +IRQCHIP_DECLARE(ixic, "phytium,ixic", phytium_ixic_dt_init); + +#ifdef CONFIG_ACPI +static int phytium_ixic_acpi_probe(struct platform_device *pdev) +{ + struct irq_domain *domain; + struct ixic_irq_data *data; + struct resource *ctr, *hpb; + + ctr = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!ctr) { + dev_err(&pdev->dev, "failed to parse 'ctr' memory resource\n"); + return -ENXIO; + } + + hpb = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!hpb) { + dev_err(&pdev->dev, "failed to parse 'hpb' memory resource\n"); + return -ENXIO; + } + + data = phytium_ixic_init(dev_fwnode(&pdev->dev), ctr, hpb); + if (IS_ERR(data)) + return PTR_ERR(data); + + domain = acpi_irq_create_hierarchy(0, NUM_IRQS, dev_fwnode(&pdev->dev), + &ixic_domain_ops, data); + if (!domain) { + dev_err(&pdev->dev, "failed to create IRQ domain\n"); + goto out_unmap; + } + + dev_info(&pdev->dev, "%d interrupts forwarded\n", NUM_IRQS); + + return 0; + +out_unmap: + iounmap(data->ctr); + iounmap(data->hpb); + kfree(data); + return -ENOMEM; +} + +static const struct acpi_device_id phytium_ixic_acpi_ids[] = { + { "PHYT0013" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(acpi, phytium_ixic_acpi_ids); + +static struct platform_driver phytium_ixic_driver = { + .driver = { + .name = "phytium-ixic", + .acpi_match_table = phytium_ixic_acpi_ids, + }, + .probe = phytium_ixic_acpi_probe, +}; +builtin_platform_driver(phytium_ixic_driver); +#endif diff --git a/target/linux/phytium/files-5.10/drivers/mailbox/phytium-mailbox.c b/target/linux/phytium/files-5.10/drivers/mailbox/phytium-mailbox.c new file mode 100644 index 000000000..21549e25b --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/mailbox/phytium-mailbox.c @@ -0,0 +1,214 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Phytium SoC mailbox driver + * + * Copyright (c) 2020-2023 Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define INTR_STAT 0x0 +#define INTR_SET 0x8 +#define INTR_CLR 0x10 + +#define TX_REG 0x100 + +#define NR_CHANS 1 + +static spinlock_t lock; + +struct phytium_mbox_link { + unsigned int irq; + void __iomem *tx_reg; + void __iomem *rx_reg; +}; + +struct phytium_mbox { + void __iomem *base; + struct phytium_mbox_link mlink; + struct mbox_chan chan; + struct mbox_controller mbox; +}; + +static irqreturn_t phytium_mbox_rx_irq(int irq, void *ch) +{ + struct mbox_chan *chan = ch; + struct phytium_mbox_link *mlink = chan->con_priv; + u32 val; + unsigned long flags = 0; + + spin_lock_irqsave(&lock, flags); + val = readl_relaxed(mlink->rx_reg + INTR_STAT); + spin_unlock_irqrestore(&lock, flags); + if (!val) + return IRQ_NONE; + + mbox_chan_received_data(chan, (void *)&val); + + spin_lock_irqsave(&lock, flags); + writel_relaxed(val, mlink->rx_reg + INTR_CLR); + spin_unlock_irqrestore(&lock, flags); + + return IRQ_HANDLED; +} + +static int phytium_mbox_send_data(struct mbox_chan *chan, void *data) +{ + struct phytium_mbox_link *mlink = chan->con_priv; + u32 *arg = data; + unsigned long flags = 0; + + spin_lock_irqsave(&lock, flags); + writel_relaxed(*arg, mlink->tx_reg + INTR_SET); + spin_unlock_irqrestore(&lock, flags); + + return 0; +} + +static int phytium_mbox_startup(struct mbox_chan *chan) +{ + struct phytium_mbox_link *mlink = chan->con_priv; + u32 val; + int ret; + unsigned long flags = 0; + + spin_lock_irqsave(&lock, flags); + val = readl_relaxed(mlink->tx_reg + INTR_STAT); + writel_relaxed(val, mlink->tx_reg + INTR_CLR); + spin_unlock_irqrestore(&lock, flags); + + ret = request_irq(mlink->irq, phytium_mbox_rx_irq, + IRQF_SHARED, "phytium_mbox_link", chan); + if (ret) { + dev_err(chan->mbox->dev, + "Unable to acquire IRQ %d\n", mlink->irq); + } + + return ret; +} + +static void phytium_mbox_shutdown(struct mbox_chan *chan) +{ + struct phytium_mbox_link *mlink = chan->con_priv; + + free_irq(mlink->irq, chan); +} + +static bool phytium_mbox_last_tx_done(struct mbox_chan *chan) +{ + unsigned long flags; + struct phytium_mbox_link *mlink = chan->con_priv; + u32 val; + + spin_lock_irqsave(&lock, flags); + val = readl_relaxed(mlink->tx_reg + INTR_STAT); + spin_unlock_irqrestore(&lock, flags); + + return (val == (u32)(1U << 31)); +} + +static const struct mbox_chan_ops phytium_mbox_ops = { + .send_data = phytium_mbox_send_data, + .startup = phytium_mbox_startup, + .shutdown = phytium_mbox_shutdown, + .last_tx_done = phytium_mbox_last_tx_done, +}; + +#ifdef CONFIG_ACPI +static const struct acpi_device_id phytium_mbox_acpi_match[] = { + { "PHYT0009", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, phytium_mbox_acpi_match); +#endif + +static const struct of_device_id phytium_mbox_of_match[] = { + { .compatible = "phytium,mbox", }, + { }, +}; +MODULE_DEVICE_TABLE(of, phytium_mbox_of_match); + +static int phytium_mbox_probe(struct platform_device *pdev) +{ + struct phytium_mbox *mbox; + struct resource *res; + int err, irq; + + spin_lock_init(&lock); + + /* Allocate memory for device */ + mbox = devm_kzalloc(&pdev->dev, sizeof(*mbox), GFP_KERNEL); + if (!mbox) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mbox->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(mbox->base)) { + dev_err(&pdev->dev, "ioremap base failed\n"); + return PTR_ERR(mbox->base); + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "cannot obtain irq\n"); + return irq; + } + + mbox->chan.con_priv = &mbox->mlink; + mbox->mlink.irq = irq; + mbox->mlink.rx_reg = mbox->base; + mbox->mlink.tx_reg = mbox->mlink.rx_reg + TX_REG; + + mbox->mbox.dev = &pdev->dev; + mbox->mbox.chans = &mbox->chan; + mbox->mbox.num_chans = NR_CHANS; + mbox->mbox.ops = &phytium_mbox_ops; + mbox->mbox.txdone_irq = false; + mbox->mbox.txdone_poll = true; + mbox->mbox.txpoll_period = 1; + + platform_set_drvdata(pdev, mbox); + + err = mbox_controller_register(&mbox->mbox); + if (err) { + dev_err(&pdev->dev, "Failed to register mailboxes %d\n", err); + goto fail; + } + + dev_info(&pdev->dev, "Phytium SoC Mailbox registered\n"); +fail: + return err; +} + +static int phytium_mbox_remove(struct platform_device *pdev) +{ + struct phytium_mbox *mbox = platform_get_drvdata(pdev); + + mbox_controller_unregister(&mbox->mbox); + + return 0; +} + +static struct platform_driver phytium_mbox_driver = { + .probe = phytium_mbox_probe, + .remove = phytium_mbox_remove, + .driver = { + .name = "phytium-mbox", + .of_match_table = of_match_ptr(phytium_mbox_of_match), + .acpi_match_table = ACPI_PTR(phytium_mbox_acpi_match), + }, +}; + +module_platform_driver(phytium_mbox_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Phytium SoC Mailbox Driver"); +MODULE_AUTHOR("Chen Baozi "); diff --git a/target/linux/phytium/files-5.10/drivers/media/platform/phytium-jpeg/Makefile b/target/linux/phytium/files-5.10/drivers/media/platform/phytium-jpeg/Makefile new file mode 100644 index 000000000..d9f50a1aa --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/media/platform/phytium-jpeg/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only +phytium_jpeg-objs := phytium_jpeg_core.o +obj-$(CONFIG_VIDEO_PHYTIUM_JPEG) += phytium_jpeg.o diff --git a/target/linux/phytium/files-5.10/drivers/media/platform/phytium-jpeg/phytium_jpeg_core.c b/target/linux/phytium/files-5.10/drivers/media/platform/phytium-jpeg/phytium_jpeg_core.c new file mode 100644 index 000000000..b0324a6d0 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/media/platform/phytium-jpeg/phytium_jpeg_core.c @@ -0,0 +1,1379 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for Phytium JPEG Encoder Engine + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#include "phytium_jpeg_reg.h" +#include "phytium_jpeg_core.h" +#include + +static u32 phytium_jpeg_header[PHYTIUM_JPEG_HEADER_SIZE] = { + 0xe0ffd8ff, 0x464a0100, 0x01004649, 0x01000001, + 0x00000100, 0x4300dbff, 0x0c0b1000, 0x100a0c0e, + 0x120e0d0e, 0x18131011, 0x16181a28, 0x23311816, + 0x3a281d25, 0x393c3d33, 0x40373833, 0x404e5c48, + 0x37455744, 0x516d5038, 0x67625f57, 0x4d3e6768, + 0x64707971, 0x67655c78, 0x00dbff63, 0x12110143, + 0x18151812, 0x2f1a1a2f, 0x42384263, 0x63636363, + 0x63636363, 0x63636363, 0x63636363, 0x63636363, + 0x63636363, 0x63636363, 0x63636363, 0x63636363, + 0x63636363, 0x63636363, 0x63636363, 0xc0ff6363, + /* h_index(40) indicates high 8 bits of the height + * w_index(41) contains the low 8 bits of the height, + * and the width. For example, height 480(0x01e0) + * locates at 0x<01> 081100 and 0x038002 . + * width 640 (0x0280) locates at 0x03 <80> <02> e0. + */ + + /* 0x0200 <11> 01 is a field marks YUV mode */ + 0x01081100, 0x038002e0, 0x02001101, 0x11030111, + 0x00c4ff01, 0x0100001f, 0x01010105, 0x00010101, + 0x00000000, 0x01000000, 0x05040302, 0x09080706, + 0xc4ff0b0a, 0x00011f00, 0x01010103, 0x01010101, + 0x00000101, 0x00000000, 0x04030201, 0x08070605, + 0xff0b0a09, 0x10b500c4, 0x03010200, 0x03040203, + 0x04040505, 0x7d010000, 0x00030201, 0x12051104, + 0x06413121, 0x07615113, 0x32147122, 0x08a19181, + 0xc1b14223, 0xf0d15215, 0x72623324, 0x160a0982, + 0x1a191817, 0x28272625, 0x35342a29, 0x39383736, + 0x4544433a, 0x49484746, 0x5554534a, 0x59585756, + 0x6564635a, 0x69686766, 0x7574736a, 0x79787776, + 0x8584837a, 0x89888786, 0x9493928a, 0x98979695, + 0xa3a29a99, 0xa7a6a5a4, 0xb2aaa9a8, 0xb6b5b4b3, + 0xbab9b8b7, 0xc5c4c3c2, 0xc9c8c7c6, 0xd4d3d2ca, + 0xd8d7d6d5, 0xe2e1dad9, 0xe6e5e4e3, 0xeae9e8e7, + 0xf4f3f2f1, 0xf8f7f6f5, 0xc4fffaf9, 0x0011b500, + 0x04020102, 0x07040304, 0x00040405, 0x00770201, + 0x11030201, 0x31210504, 0x51411206, 0x13716107, + 0x08813222, 0xa1914214, 0x2309c1b1, 0x15f05233, + 0x0ad17262, 0xe1342416, 0x1817f125, 0x27261a19, + 0x352a2928, 0x39383736, 0x4544433a, 0x49484746, + 0x5554534a, 0x59585756, 0x6564635a, 0x69686766, + 0x7574736a, 0x79787776, 0x8483827a, 0x88878685, + 0x93928a89, 0x97969594, 0xa29a9998, 0xa6a5a4a3, + 0xaaa9a8a7, 0xb5b4b3b2, 0xb9b8b7b6, 0xc4c3c2ba, + 0xc8c7c6c5, 0xd3d2cac9, 0xd7d6d5d4, 0xe2dad9d8, + 0xe6e5e4e3, 0xeae9e8e7, 0xf5f4f3f2, 0xf9f8f7f6, + 0x00fefffa, 0x0000008f, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xdaff0000, 0x01030c00, 0x03110200, 0x003f0011 +}; + +static char yuv_mode_str[YUV_MODE_STR_LEN] = { "yuv444" }; + +module_param_string(yuv_mode, yuv_mode_str, sizeof(yuv_mode_str), 0444); +MODULE_PARM_DESC(yuv_mode, "Users select one mode from such modes as 'yuv444', or 'yuv422', or 'yuv420'. If no mode is set, the driver adapts defaults mode 'yuv444'."); + +static u32 phytium_jpeg_read(struct phytium_jpeg_dev *jpeg_dev, u32 reg) +{ + u32 reg_val = readl(jpeg_dev->base_addr + reg); + + dev_dbg(jpeg_dev->dev, "read 0x%p + 0x%x -->val[0x%x]\n", + jpeg_dev->base_addr, reg, reg_val); + + return reg_val; +} + +static void phytium_jpeg_write(struct phytium_jpeg_dev *jpeg_dev, + u32 reg, u32 val) +{ + writel(val, jpeg_dev->base_addr + reg); + dev_dbg(jpeg_dev->dev, "write 0x%x to addr 0x%p + 0x%x\n", + val, jpeg_dev->base_addr, reg); +} + +static void phytium_jpeg_update(struct phytium_jpeg_dev *jpeg_dev, u32 reg, + u32 clear, u32 bits) +{ + u32 reg_val = readl(jpeg_dev->base_addr + reg); + u32 tmp = reg_val; + + reg_val &= ~clear; + reg_val |= bits; + writel(reg_val, jpeg_dev->base_addr + reg); + + dev_dbg(jpeg_dev->dev, "the val of addr 0x%p + 0x%x, from 0x%x to 0x%x\n", + jpeg_dev->base_addr, reg, tmp, readl(jpeg_dev->base_addr + reg)); +} + +static void phytium_jpeg_init_regs(struct phytium_jpeg_dev *jpeg_dev) +{ + u32 transform_info = 0; + u32 disable_all_interrupt = 0; + u32 clear_all_interrupt = INT_FIFO_OVERFLOW | INT_OCM_BUF_OVERFLOW | + INT_JPEG_ENCODE_COMPLETE | INT_VIDEO_FORMAT_CHANGE; + u32 rate_to_reg = 0; + + /* First, disable the JPEG engine, set bit0 = 0*/ + phytium_jpeg_write(jpeg_dev, TRANSFORM_INFO_REG, transform_info); + + /* Second, set VGAvideo_source_information. bit1 = 0 marks VGA */ + transform_info |= 0; + + /* Third, set AXI burst length bit[16:22]= 0xf , default value*/ + transform_info |= (0xF << TRANS_AXI_LEN_SHIFT) & TRANSINFO_AXI_LEN; + + /* Fourth, the default sampling format is YUV422, set bit13 to 0 */ + /* ignore setting sampling interval */ + phytium_jpeg_write(jpeg_dev, TRANSFORM_INFO_REG, transform_info); + udelay(5); + + /* Fifth, setting frame rate. + * Linux driver prohibit float point operations. So use the + * format: reg_val = (1 second * 10^8 / frame_rate / 134 *100) + * write reg_val to register. then enable Highest bit31 = 1 + */ + if (jpeg_dev->frame_rate) { + rate_to_reg = 100000000 / jpeg_dev->frame_rate / 134 * 100; + rate_to_reg |= FRAME_SAMPLE_CTRL_EN; + phytium_jpeg_write(jpeg_dev, FRAME_SAMPLE_CTRL, rate_to_reg); + } + /* Sixth, HUFF_MODE, driver needn't to configure, ignore */ + + /* disable all interrupts and then clear all interrupts */ + phytium_jpeg_write(jpeg_dev, INT_STATUS_CTRL_REG, + disable_all_interrupt); + udelay(5); + phytium_jpeg_write(jpeg_dev, INT_STATUS_CTRL_REG, clear_all_interrupt); + + /* Seventh, Sample_mode, hardware default is yuv444 */ + jpeg_dev->yuv420 = false; +} + +/* Turn on the clock of the jpeg engine */ +static void phytium_jpeg_on(struct phytium_jpeg_dev *jpeg_dev) +{ + if (test_bit(VIDEO_CLOCKS_ON, &jpeg_dev->status)) + return; + + /* Turn on the relevant clocks */ + set_bit(VIDEO_CLOCKS_ON, &jpeg_dev->status); +} + +/* Disable the jpeg engine */ +static void phytium_jpeg_off(struct phytium_jpeg_dev *jpeg_dev) +{ + u32 disable_all_interrupt = 0; + u32 clear_all_interrupt = INT_FIFO_OVERFLOW | INT_OCM_BUF_OVERFLOW | + INT_JPEG_ENCODE_COMPLETE | INT_VIDEO_FORMAT_CHANGE; + + if (!test_bit(VIDEO_CLOCKS_ON, &jpeg_dev->status)) { + dev_info(jpeg_dev->dev, "JPEG Engine is already off.\n"); + return; + } + + /* disable all interrupt */ + phytium_jpeg_write(jpeg_dev, INT_STATUS_CTRL_REG, disable_all_interrupt); + /* clear all interrupt */ + phytium_jpeg_write(jpeg_dev, INT_STATUS_CTRL_REG, clear_all_interrupt); + /* disable JPEG engine */ + phytium_jpeg_update(jpeg_dev, TRANSFORM_INFO_REG, TRANSINFO_ENABLE_ENGINE, 0); + + clear_bit(VIDEO_CLOCKS_ON, &jpeg_dev->status); + /* wait 50 ms */ + mdelay(50); + /* C08 bit7 1:busy */ +} + +static inline void phytium_jpeg_enable_source_detecting(struct phytium_jpeg_dev *jpeg_dev) +{ + /* + * Enable the dectection to discovery + * the source resolution is changed + */ + //phytium_jpeg_update(jpeg_dev, INT_STATUS_CTRL_REG, 0, DETECT_RESOLUTION_CHANGE_EN); + phytium_jpeg_update(jpeg_dev, TRANSFORM_INFO_REG, 0, TRANSINFO_SRC_SELECT); +} + +#define res_check(val) \ + test_and_clear_bit(VIDEO_MODE_DETECT_DONE, &(val)->status) + +static void phytium_jpeg_get_resolution(struct phytium_jpeg_dev *jpeg_dev) +{ + u32 source_info; + u32 width; + u32 height; + struct v4l2_bt_timings *detected_timings = &jpeg_dev->detected_timings; + + /* Before get a new resolution, maybe need to wait 10 us */ + detected_timings->width = MIN_WIDTH; + detected_timings->height = MIN_HEIGHT; + jpeg_dev->v4l2_input_status = V4L2_IN_ST_NO_SIGNAL; + + + phytium_jpeg_enable_source_detecting(jpeg_dev); + source_info = phytium_jpeg_read(jpeg_dev, SRC_VGA_INFO_REG); + width = (source_info & SRC_HOR_PIXELS) >> SRC_WIDTH_SHIFT; + height = (source_info & SRC_VER_PIXELS) >> SRC_HEIGHT_SHIFT; + + if (width * height != 0) { + detected_timings->width = width; + detected_timings->height = height; + } + + jpeg_dev->v4l2_input_status = 0; + + /* + * Resolution is changed will trigger an interrupt, resolution detecting + * also is disable during process interrupt. So re-enable. + */ + phytium_jpeg_enable_source_detecting(jpeg_dev); + dev_info(jpeg_dev->dev, "Change resolution: %uX%u\n", width, height); +} + +static void phytium_jpeg_set_resolution(struct phytium_jpeg_dev *jpeg_dev) +{ + struct v4l2_bt_timings *active_timings = &jpeg_dev->active_timings; + int i; + int src_addrs[OCM_BUF_NUM]; + /* + * The OCM address space is 0x30C0_0000 ~ 0x30C7_FFFF, JPEG Engine uses the + * high-bottom address. src_0 uses 0x30C4_0000 ~ 0x30c6_0000 (total capacity is + * 128KB, greater than the requirements of the largest resolution). src_1 uses + * 0x30C6_0000 ~ 0x30C7_FFFF. + */ + + /* The OCM address should shift right 8 bits */ + for (i = 0; i < OCM_BUF_NUM; i++) + src_addrs[i] = jpeg_dev->src_addrs[i].dma_addr >> OCM_BUF_SHIFT; + + phytium_jpeg_write(jpeg_dev, OCM_BUF0_ADDR, src_addrs[0]); + phytium_jpeg_write(jpeg_dev, OCM_BUF1_ADDR, src_addrs[1]); + + /* + * In the worst case, the size of one image will be compressed to 25% the + * raw image's size. When a pixel is 4-byte, no need to divide 4. + */ + jpeg_dev->max_compressed_size = active_timings->width * active_timings->height; +} + +/* The below functions is implemented for various v4l2 ioctl operations */ +static int phytium_jpeg_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct phytium_jpeg_dev *jpeg_dev = video_drvdata(file); + + strscpy(cap->driver, PHYTIUM_JPEG_NAME, sizeof(cap->driver)); + strscpy(cap->card, "Phytium JPEG Engine", sizeof(cap->card)); + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", dev_name(jpeg_dev->dev)); + + return 0; +} + +static int phytium_jpeg_enum_format(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct phytium_jpeg_dev *jpeg_dev = video_drvdata(file); + + if (f->index) { + dev_err(jpeg_dev->dev, "Failed to enum format\n"); + return -EINVAL; + } + + f->pixelformat = V4L2_PIX_FMT_JPEG; + + return 0; +} + +static int phytium_jpeg_get_format(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct phytium_jpeg_dev *jpeg_dev = video_drvdata(file); + + f->fmt.pix = jpeg_dev->pix_fmt; + + return 0; +} + +static int phytium_jpeg_enum_input(struct file *file, void *priv, + struct v4l2_input *input) +{ + struct phytium_jpeg_dev *jpeg_dev = video_drvdata(file); + + if (input->index) { + dev_err(jpeg_dev->dev, "failed to enum input\n"); + return -EINVAL; + } + + strscpy(input->name, "Host DC Capture", sizeof(input->name)); + input->type = V4L2_INPUT_TYPE_CAMERA; + input->capabilities = V4L2_IN_CAP_DV_TIMINGS; + input->status = jpeg_dev->v4l2_input_status; + + return 0; +} + +static int phytium_jpeg_get_input(struct file *file, void *priv, + unsigned int *i) +{ + *i = 0; + return 0; +} + +static int phytium_jpeg_set_input(struct file *file, void *priv, + unsigned int i) +{ + struct phytium_jpeg_dev *jpeg_dev = video_drvdata(file); + + if (i != 0) { + dev_err(jpeg_dev->dev, "Failed to set input\n"); + return -EINVAL; + } + + return 0; +} + +static int phytium_jpeg_get_parm(struct file *file, void *priv, + struct v4l2_streamparm *stream) +{ + struct phytium_jpeg_dev *jpeg_dev = video_drvdata(file); + /* Readbuffers num is 3 */ + stream->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; + stream->parm.capture.readbuffers = CAPTURE_BUF_NUMBER; + stream->parm.capture.timeperframe.denominator = 1; + + if (jpeg_dev->frame_rate == 0) + stream->parm.capture.timeperframe.denominator = MAX_FRAME_RATE; + else + stream->parm.capture.timeperframe.denominator = jpeg_dev->frame_rate; + + return 0; +} + +static int phytium_jpeg_set_parm(struct file *file, void *priv, + struct v4l2_streamparm *stream) +{ + unsigned int frame_rate = 0; + u32 rate_to_reg = 0; + struct phytium_jpeg_dev *jpeg_dev = video_drvdata(file); + /* Readbuffers num is 3 */ + stream->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; + stream->parm.capture.readbuffers = CAPTURE_BUF_NUMBER; + + if (stream->parm.capture.timeperframe.numerator) + frame_rate = stream->parm.capture.timeperframe.denominator / + stream->parm.capture.timeperframe.numerator; + + if (frame_rate == 0 || frame_rate > MAX_FRAME_RATE) { + frame_rate = MAX_FRAME_RATE; + stream->parm.capture.timeperframe.denominator = MAX_FRAME_RATE; + stream->parm.capture.timeperframe.numerator = 1; + } + /* + * reg_val = (1 second * 10^9 / frame_rate / 13.4) + * Linux driver prohibit float point operations. So use the + * format: reg_val = (1 second * 10^8 / frame_rate / 134 *100) + * write reg_val to register. then enable Highest bit31 = 1 + */ + if (jpeg_dev->frame_rate != frame_rate) { + jpeg_dev->frame_rate = frame_rate; + rate_to_reg = 100000000 / jpeg_dev->frame_rate / 134 * 100; + rate_to_reg |= FRAME_SAMPLE_CTRL_EN; + phytium_jpeg_write(jpeg_dev, FRAME_SAMPLE_CTRL, rate_to_reg); + } + + return 0; +} + +static int phytium_jpeg_enum_framesizes(struct file *file, void *priv, + struct v4l2_frmsizeenum *fsize) + +{ + struct phytium_jpeg_dev *jpeg_dev = video_drvdata(file); + + if (fsize->index != 0) { + dev_err(jpeg_dev->dev, "Failed to enum framesize.\n"); + return -EINVAL; + } + + if (fsize->pixel_format != V4L2_PIX_FMT_JPEG) { + dev_err(jpeg_dev->dev, "enum framesize pixel_format is not JPEG"); + return -EINVAL; + } + + fsize->discrete.width = jpeg_dev->pix_fmt.width; + fsize->discrete.height = jpeg_dev->pix_fmt.height; + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + + return 0; + +} + +static int phytium_jpeg_enum_frameintervals(struct file *file, void *priv, + struct v4l2_frmivalenum *fival) +{ + struct phytium_jpeg_dev *jpeg_dev = video_drvdata(file); + + if (fival->index != 0) { + dev_err(jpeg_dev->dev, "enum frame intervals failed\n"); + return -EINVAL; + } + + if (fival->width != jpeg_dev->detected_timings.width || + fival->height != jpeg_dev->detected_timings.height) { + dev_err(jpeg_dev->dev, "interval isn't same with the detected_timings.\n"); + return -EINVAL; + } + + if (fival->pixel_format != V4L2_PIX_FMT_JPEG) { + dev_err(jpeg_dev->dev, "enum frame interval pixel fomat is incorrect.\n"); + return -EINVAL; + } + + fival->type = V4L2_FRMIVAL_TYPE_CONTINUOUS; + fival->stepwise.min.denominator = MAX_FRAME_RATE; + fival->stepwise.min.numerator = 1; + fival->stepwise.max.denominator = 1; + fival->stepwise.max.numerator = 1; + fival->stepwise.step = fival->stepwise.max; + + return 0; +} + +static int phytium_jpeg_set_dv_timings(struct file *file, void *priv, + struct v4l2_dv_timings *timings) +{ + struct phytium_jpeg_dev *jpeg_dev = video_drvdata(file); + + /* the params are passed from user space are same with hardware's params */ + if (timings->bt.width == jpeg_dev->active_timings.width && + timings->bt.height == jpeg_dev->active_timings.height) + return 0; + + if (vb2_is_busy(&jpeg_dev->queue)) { + dev_err(jpeg_dev->dev, "queue is busy during setting dv timings.\n"); + return -EBUSY; + } + + jpeg_dev->active_timings = timings->bt; + phytium_jpeg_set_resolution(jpeg_dev); + jpeg_dev->pix_fmt.width = timings->bt.width; + jpeg_dev->pix_fmt.height = timings->bt.height; + jpeg_dev->pix_fmt.sizeimage = jpeg_dev->max_compressed_size; + timings->type = V4L2_DV_BT_656_1120; + + return 0; +} + +static int phytium_jpeg_get_dv_timings(struct file *file, void *priv, + struct v4l2_dv_timings *timings) +{ + struct phytium_jpeg_dev *jpeg_dev = video_drvdata(file); + + timings->type = V4L2_DV_BT_656_1120; + timings->bt = jpeg_dev->active_timings; + + return 0; +} + +static int phytium_jpeg_query_dv_timings(struct file *file, void *priv, + struct v4l2_dv_timings *timings) +{ + int ret; + struct phytium_jpeg_dev *jpeg_dev = video_drvdata(file); + + /* + * This blocks only if the driver is currently in the process of + * detecting a new resolution; in the event of no signal or timeout + * this function is woken up. + */ + if ((file->f_flags & O_NONBLOCK) && + test_bit(VIDEO_RES_CHANGE, &jpeg_dev->status)) + return -EAGAIN; + + ret = wait_event_interruptible(jpeg_dev->wait, !test_bit(VIDEO_RES_CHANGE, + &jpeg_dev->status)); + if (ret) { + dev_err(jpeg_dev->dev, "Failed to query dv timing\n"); + return -EINTR; + } + + timings->type = V4L2_DV_BT_656_1120; + timings->bt = jpeg_dev->detected_timings; + + return jpeg_dev->v4l2_input_status ? -ENOLINK : 0; +} + +static const struct v4l2_dv_timings_cap phytium_jpeg_timings_cap = { + .type = V4L2_DV_BT_656_1120, + .bt = { + .min_width = MIN_WIDTH, + .max_width = MAX_WIDTH, + .min_height = MIN_HEIGHT, + .max_height = MAX_HEIGHT, + .min_pixelclock = 6574080, /* 640 x 480 x 24Hz */ + .max_pixelclock = 1244160000, /* 1920 x 1080 x 60Hz */ + .standards = V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | + V4L2_DV_BT_STD_CVT | V4L2_DV_BT_STD_GTF, + .capabilities = V4L2_DV_BT_CAP_PROGRESSIVE | + V4L2_DV_BT_CAP_REDUCED_BLANKING | + V4L2_DV_BT_CAP_CUSTOM, + }, +}; + +static int phytium_jpeg_enum_dv_timings(struct file *file, void *priv, + struct v4l2_enum_dv_timings *timings) +{ + return v4l2_enum_dv_timings_cap(timings, &phytium_jpeg_timings_cap, + NULL, NULL); +} + +static int phytium_jpeg_dv_timings_cap(struct file *file, void *priv, + struct v4l2_dv_timings_cap *cap) +{ + *cap = phytium_jpeg_timings_cap; + + return 0; +} + +/* The function is used to notify DV that video resolution is altered */ +static int phytium_jpeg_sub_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_SOURCE_CHANGE: + return v4l2_src_change_event_subscribe(fh, sub); + default: + break; + } + + return v4l2_ctrl_subscribe_event(fh, sub); +} + +static const struct v4l2_ioctl_ops phytium_jpeg_ioctl_ops = { + .vidioc_querycap = phytium_jpeg_querycap, + .vidioc_enum_fmt_vid_cap = phytium_jpeg_enum_format, + .vidioc_g_fmt_vid_cap = phytium_jpeg_get_format, + .vidioc_s_fmt_vid_cap = phytium_jpeg_get_format, + .vidioc_try_fmt_vid_cap = phytium_jpeg_get_format, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_enum_input = phytium_jpeg_enum_input, + .vidioc_g_input = phytium_jpeg_get_input, + .vidioc_s_input = phytium_jpeg_set_input, + .vidioc_g_parm = phytium_jpeg_get_parm, + .vidioc_s_parm = phytium_jpeg_set_parm, + .vidioc_enum_framesizes = phytium_jpeg_enum_framesizes, + .vidioc_enum_frameintervals = phytium_jpeg_enum_frameintervals, + .vidioc_s_dv_timings = phytium_jpeg_set_dv_timings, + .vidioc_g_dv_timings = phytium_jpeg_get_dv_timings, + .vidioc_query_dv_timings = phytium_jpeg_query_dv_timings, + .vidioc_enum_dv_timings = phytium_jpeg_enum_dv_timings, + .vidioc_dv_timings_cap = phytium_jpeg_dv_timings_cap, + .vidioc_subscribe_event = phytium_jpeg_sub_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static void phytium_jpeg_init_jpeg_quant(struct phytium_jpeg_dev *jpeg_dev) +{ + const u32 y_quant_table[QUANT_REG_NUM] = { + 0x08000000, 0x0ba2e8ba, 0x0aaaaaab, 0x09249249, 0x0aaaaaab, + 0x0ccccccc, 0x08000000, 0x09249249, 0x09d89d8a, 0x09249249, + 0x071c71c7, 0x07878788, 0x08000000, 0x06bca1af, 0x05555555, + 0x03333333, 0x04ec4ec5, 0x05555555, 0x05d1745d, 0x05d1745d, + 0x05555555, 0x029cbc15, 0x03a83a84, 0x03759f23, 0x0469ee58, + 0x03333333, 0x0234f72c, 0x02828283, 0x02192e2a, 0x02222222, + 0x023ee090, 0x02828283, 0x02492492, 0x0253c825, 0x02000000, + 0x01c71c72, 0x01642c86, 0x01a41a42, 0x02000000, 0x01e1e1e2, + 0x0178a4c8, 0x01dae607, 0x0253c825, 0x02492492, 0x0199999a, + 0x012c9fb5, 0x01948b10, 0x0178a4c8, 0x0158ed23, 0x014e5e0a, + 0x013e22cc, 0x013b13b1, 0x013e22cc, 0x02108421, 0x01a98ef6, + 0x0121fb78, 0x010ecf57, 0x01249249, 0x013e22cc, 0x01111111, + 0x01642c86, 0x01446f86, 0x013e22cc, 0x014afd6a + }; + + const u32 c_quant_table[QUANT_REG_NUM] = { + 0x07878788, 0x071c71c7, 0x071c71c7, 0x05555555, 0x06186186, + 0x05555555, 0x02b93105, 0x04ec4ec5, 0x04ec4ec5, 0x02b93105, + 0x014afd6a, 0x01f07c1f, 0x02492492, 0x01f07c1f, 0x014afd6a, + 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a, + 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a, + 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a, + 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a, + 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a, + 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a, + 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a, + 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a, + 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a, + 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a + }; + int i; + + for (i = 0; i < QUANT_REG_NUM; i++) { + phytium_jpeg_write(jpeg_dev, Y_QUANT_INDEX_ADDR_REG(i), y_quant_table[i]); + phytium_jpeg_write(jpeg_dev, C_QUANT_INDEX_ADDR_REG(i), c_quant_table[i]); + } + +} + +static void phytium_jpeg_start(struct phytium_jpeg_dev *jpeg_dev) +{ + phytium_jpeg_on(jpeg_dev); + phytium_jpeg_init_regs(jpeg_dev); + + /* Resolution set to 640x480 if no signal is found */ + phytium_jpeg_get_resolution(jpeg_dev); + + /* Set timings since the device is being opened for the first tiime */ + jpeg_dev->active_timings = jpeg_dev->detected_timings; + phytium_jpeg_set_resolution(jpeg_dev); + + jpeg_dev->pix_fmt.width = jpeg_dev->active_timings.width; + jpeg_dev->pix_fmt.height = jpeg_dev->active_timings.height; + jpeg_dev->pix_fmt.sizeimage = jpeg_dev->max_compressed_size; +} + +static void phytium_jpeg_stop(struct phytium_jpeg_dev *jpeg_dev) +{ + set_bit(VIDEO_STOPPED, &jpeg_dev->status); + cancel_delayed_work_sync(&jpeg_dev->res_work); + + phytium_jpeg_off(jpeg_dev); + + jpeg_dev->v4l2_input_status = V4L2_IN_ST_NO_SIGNAL; + jpeg_dev->status = 0; +} + +static int phytium_jpeg_open(struct file *file) +{ + int ret; + struct phytium_jpeg_dev *jpeg_dev = video_drvdata(file); + + mutex_lock(&jpeg_dev->video_lock); + + ret = v4l2_fh_open(file); + if (ret != 0) { + mutex_unlock(&jpeg_dev->video_lock); + dev_err(jpeg_dev->dev, "Failed to open the phytium jpeg device.\n"); + return ret; + } + + if (v4l2_fh_is_singular_file(file)) + phytium_jpeg_start(jpeg_dev); + + mutex_unlock(&jpeg_dev->video_lock); + + return 0; +} + +static int phytium_jpeg_release(struct file *file) +{ + int ret; + struct phytium_jpeg_dev *jpeg_dev = video_drvdata(file); + + mutex_lock(&jpeg_dev->video_lock); + + if (v4l2_fh_is_singular_file(file)) + phytium_jpeg_stop(jpeg_dev); + + ret = _vb2_fop_release(file, NULL); + mutex_unlock(&jpeg_dev->video_lock); + + return ret; +} + + +static const struct v4l2_file_operations phytium_jpeg_fops = { + .owner = THIS_MODULE, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = vb2_fop_mmap, + .open = phytium_jpeg_open, + .release = phytium_jpeg_release, +}; + +static void phytium_jpeg_update_jpeg_header(u32 width, u32 height) +{ + const int h_index = PHYTIUM_JPEG_HEADER_H_INDEX; + const int w_index = PHYTIUM_JPEG_HEADER_W_INDEX; + + /* the high 8 bits of the height locates at bit24~bit31 */ + phytium_jpeg_header[h_index] = phytium_jpeg_header[h_index] & 0x00FFFFFF; + phytium_jpeg_header[h_index] |= ((height >> 8) & 0xFF) << 24; + + /* the low 8 bits of the height locates at bit0~bit7 */ + phytium_jpeg_header[w_index] = phytium_jpeg_header[w_index] & 0xFF000000; + phytium_jpeg_header[w_index] |= height & 0xFF; + + /* the high 8 bits of the width locates at bit8~bit15 */ + phytium_jpeg_header[w_index] |= ((width >> 8) & 0xFF) << 8; + /* the low 8 bits of the width locates at bit16~bit24 */ + phytium_jpeg_header[w_index] |= (width & 0xFF) << 16; +} + +static void phytium_jpeg_fill_header(struct phytium_jpeg_dev *jpeg_dev, + struct phytium_jpeg_buffer *jpeg_buf) +{ + void *vbuf = vb2_plane_vaddr(&jpeg_buf->vb.vb2_buf, 0); + u32 width = jpeg_dev->active_timings.width; + u32 height = jpeg_dev->active_timings.height; + + /* update the contents of the phytium jpeg header according to the resolution */ + phytium_jpeg_update_jpeg_header(width, height); + + /* replenish the contents of the JPEG header */ + memcpy(vbuf, phytium_jpeg_header, PHYTIUM_JPEG_HEADER_LEN); +} + +static int phytium_jpeg_start_frame(struct phytium_jpeg_dev *jpeg_dev) +{ + dma_addr_t dst_addr; + unsigned long status; + struct phytium_jpeg_buffer *jpeg_buf; + + if (jpeg_dev->v4l2_input_status) { + dev_err(jpeg_dev->dev, "No signal; needn't start frame\n"); + return 0; + } + + spin_lock_irqsave(&jpeg_dev->hw_lock, status); + jpeg_buf = list_first_entry_or_null(&jpeg_dev->buffers, + struct phytium_jpeg_buffer, link); + if (jpeg_buf == NULL) { + spin_unlock_irqrestore(&jpeg_dev->hw_lock, status); + dev_err(jpeg_dev->dev, "No buffers; doesn't start frame\n"); + return -EPROTO; + } + + set_bit(VIDEO_FRAME_INPRG, &jpeg_dev->status); + dst_addr = vb2_dma_contig_plane_dma_addr(&jpeg_buf->vb.vb2_buf, 0); + spin_unlock_irqrestore(&jpeg_dev->hw_lock, status); + + /* + * Because the JPEG Engine is unable to add a JPEG header, the phytium + * jpeg driver is required to fill the contents of a JPEG header before + * the jpeg engine write datas to the dma address. + */ + phytium_jpeg_fill_header(jpeg_dev, jpeg_buf); + dst_addr += PHYTIUM_JPEG_HEADER_LEN; + /* + * The ikvm application only using the last frame, so the driver replenish + * one output register with a dma address. + */ + dst_addr >>= JPEG_DST_ADDR_SHIFT; + phytium_jpeg_write(jpeg_dev, BUF_LIST_INDEX_ADDR(VB_BUF_NO), dst_addr); + /* Enable the validilty of the buffer marked with index */ + phytium_jpeg_write(jpeg_dev, BUF_LIST_INDEX_CTRL_STS_ADDR(VB_BUF_NO), + STS_JPEG_BUF_HIGH_LEVEL_VALID); + /* Enable the interruption which is used to identify an image was compressed */ + phytium_jpeg_update(jpeg_dev, INT_STATUS_CTRL_REG, 0, STS_VE_JPEG_CODE_COMP_EN); + /* Enable JPEG, start to capture and compress */ + phytium_jpeg_update(jpeg_dev, TRANSFORM_INFO_REG, TRANSINFO_ENABLE_ENGINE, 1); + + return 0; +} + +static void phytium_jpeg_resolution_work(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct phytium_jpeg_dev *jpeg_dev = container_of(dwork, + struct phytium_jpeg_dev, res_work); + u32 input_status = jpeg_dev->v4l2_input_status; + + phytium_jpeg_on(jpeg_dev); + + /* Exit early in the case no clients remain */ + if (test_bit(VIDEO_STOPPED, &jpeg_dev->status)) + goto done; + + phytium_jpeg_init_regs(jpeg_dev); + phytium_jpeg_get_resolution(jpeg_dev); + + /* if source's resolution is changed, the event should be enqueued */ + if (jpeg_dev->detected_timings.width != jpeg_dev->active_timings.width || + jpeg_dev->detected_timings.height != jpeg_dev->active_timings.height || + input_status != jpeg_dev->v4l2_input_status) { + + static const struct v4l2_event event = { + .type = V4L2_EVENT_SOURCE_CHANGE, + .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, + }; + v4l2_event_queue(&jpeg_dev->vdev, &event); + clear_bit(VIDEO_FRAME_INPRG, &jpeg_dev->status); + dev_info(jpeg_dev->dev, "event notifies changing resolution\n"); + } else if (test_bit(VIDEO_STREAMING, &jpeg_dev->status)) { + /* No resolution change so just restart streaming */ + dev_info(jpeg_dev->dev, "resolution doesn't change\n"); + phytium_jpeg_set_resolution(jpeg_dev); + phytium_jpeg_start_frame(jpeg_dev); + } + +done: + clear_bit(VIDEO_RES_CHANGE, &jpeg_dev->status); + wake_up_interruptible_all(&jpeg_dev->wait); +} + +static int phytium_jpeg_queue_setup(struct vb2_queue *q, + unsigned int *num_buffers, + unsigned int *num_planes, + unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct phytium_jpeg_dev *jpeg_dev = vb2_get_drv_priv(q); + + if (*num_planes) { + if (sizes[0] < jpeg_dev->max_compressed_size) { + v4l2_err(&jpeg_dev->v4l2_dev, "queue v4l2_buf's size is invalid\n"); + return -EINVAL; + } + } + + *num_planes = 1; + sizes[0] = jpeg_dev->max_compressed_size; + return 0; +} + +static int phytium_jpeg_buf_prepare(struct vb2_buffer *vb) +{ + struct phytium_jpeg_dev *jpeg_dev = vb2_get_drv_priv(vb->vb2_queue); + + if (vb2_plane_size(vb, 0) < jpeg_dev->max_compressed_size) { + v4l2_err(&jpeg_dev->v4l2_dev, "failed to prepare buffer\n"); + return -EINVAL; + } + + return 0; +} + +static inline struct phytium_jpeg_buffer * +phytium_vb2buf_to_dstbuf(struct vb2_v4l2_buffer *buf) +{ + return container_of(buf, struct phytium_jpeg_buffer, vb); +} + +static void phytium_jpeg_buf_queue(struct vb2_buffer *vb) +{ + bool empty; + struct phytium_jpeg_dev *jpeg_dev = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct phytium_jpeg_buffer *jpeg_buf = phytium_vb2buf_to_dstbuf(vbuf); + unsigned long status; + + spin_lock_irqsave(&jpeg_dev->hw_lock, status); + empty = list_empty(&jpeg_dev->buffers); + list_add_tail(&jpeg_buf->link, &jpeg_dev->buffers); + spin_unlock_irqrestore(&jpeg_dev->hw_lock, status); + + /* the empty ensures the address of the first node's vb2_v4l2_buf + * in the list is written to output register + */ + if (test_bit(VIDEO_STREAMING, &jpeg_dev->status) && + (!test_bit(VIDEO_FRAME_INPRG, &jpeg_dev->status)) && + empty) + phytium_jpeg_start_frame(jpeg_dev); +} + +static void phytium_jpeg_bufs_done(struct phytium_jpeg_dev *jpeg_dev, + enum vb2_buffer_state state) +{ + unsigned long flags; + struct phytium_jpeg_buffer *buf; + + spin_lock_irqsave(&jpeg_dev->hw_lock, flags); + + list_for_each_entry(buf, &jpeg_dev->buffers, link) + vb2_buffer_done(&buf->vb.vb2_buf, state); + + INIT_LIST_HEAD(&jpeg_dev->buffers); + + spin_unlock_irqrestore(&jpeg_dev->hw_lock, flags); +} + +static void phytium_jpeg_irq_res_change(struct phytium_jpeg_dev *jpeg_dev, + ulong delay) +{ + dev_info(jpeg_dev->dev, "Source resolution is changed, resetting\n"); + set_bit(VIDEO_RES_CHANGE, &jpeg_dev->status); + + phytium_jpeg_off(jpeg_dev); + + schedule_delayed_work(&jpeg_dev->res_work, delay); +} + +static irqreturn_t phytium_jpeg_irq(int irq, void *arg) +{ + struct phytium_jpeg_dev *jpeg_dev = arg; + u32 status; + struct phytium_jpeg_buffer *buf; + u32 frame_size; + + if (test_bit(VIDEO_POWEROFF, &jpeg_dev->status)) { + dev_info(jpeg_dev->dev, "jpeg engine is requested to poweroff\n"); + return IRQ_HANDLED; + } + + status = phytium_jpeg_read(jpeg_dev, INT_STATUS_CTRL_REG); + + if (status & INT_VIDEO_FORMAT_CHANGE) { + dev_info(jpeg_dev->dev, "receive resolution changed interrupt\n"); + phytium_jpeg_update(jpeg_dev, INT_STATUS_CTRL_REG, + DETECT_RESOLUTION_CHANGE_EN, 0); + phytium_jpeg_write(jpeg_dev, INT_STATUS_CTRL_REG, INT_VIDEO_FORMAT_CHANGE); + phytium_jpeg_irq_res_change(jpeg_dev, RESOLUTION_CHANGE_DELAY); + return IRQ_HANDLED; + } + + /* + * JPEG engine finish compressing a image JPEG encoding to trigger + * a interruption. the status identifies the buffer number. Currently, + * the driver uses one buffer. + * + * Note: Because the JPEG doesn't support adding a JPEG header, and + * driver is also unable to add a JPEG header to vb2_buffers. One + * solution is that a JPEG header is added by an application. + */ + if (status & INT_JPEG_COMP_BUF_LIST_NO) { + frame_size = phytium_jpeg_read(jpeg_dev, jpeg_dev->comp_size_read); + frame_size &= JPEG_BUF_CAPACITY_SIZE; + frame_size >>= JPEG_BUF_CAPACITY_SIZE_SHIFT; + spin_lock(&jpeg_dev->hw_lock); + clear_bit(VIDEO_FRAME_INPRG, &jpeg_dev->status); + /* Delete first node from the queue */ + buf = list_first_entry_or_null(&jpeg_dev->buffers, + struct phytium_jpeg_buffer, link); + if (buf != NULL) { + frame_size += PHYTIUM_JPEG_HEADER_LEN; + vb2_set_plane_payload(&buf->vb.vb2_buf, 0, frame_size); + if (!list_is_last(&buf->link, &jpeg_dev->buffers)) { + buf->vb.vb2_buf.timestamp = ktime_get_ns(); + buf->vb.sequence = jpeg_dev->sequence++; + buf->vb.field = V4L2_FIELD_NONE; + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); + list_del(&buf->link); + } + } + + spin_unlock(&jpeg_dev->hw_lock); + /* Disable JPEG engine */ + phytium_jpeg_update(jpeg_dev, TRANSFORM_INFO_REG, TRANSINFO_ENABLE_ENGINE, 0); + /* Disable interruption */ + phytium_jpeg_update(jpeg_dev, INT_STATUS_CTRL_REG, STS_VE_JPEG_CODE_COMP_EN, 0); + /* clear all interruption of the hardware's buffers */ + phytium_jpeg_update(jpeg_dev, INT_STATUS_CTRL_REG, INT_JPEG_ENCODE_COMPLETE, 1); + + status &= ~INT_JPEG_COMP_BUF_LIST_NO; + if (test_bit(VIDEO_STREAMING, &jpeg_dev->status) && buf) + phytium_jpeg_start_frame(jpeg_dev); + } + + return IRQ_HANDLED; +} + +/* VIDIOC_STREAMON, all vb2_v4l2_buf' states are queue */ +static int phytium_jpeg_start_streaming(struct vb2_queue *q, unsigned int count) +{ + int ret; + struct phytium_jpeg_dev *jpeg_dev = vb2_get_drv_priv(q); + + jpeg_dev->sequence = 0; + ret = phytium_jpeg_start_frame(jpeg_dev); + if (ret != 0) { + phytium_jpeg_bufs_done(jpeg_dev, VB2_BUF_STATE_QUEUED); + return ret; + } + + /* set the states of the jpeg engine */ + set_bit(VIDEO_STREAMING, &jpeg_dev->status); + return ret; +} + +static void phytium_jpeg_stop_streaming(struct vb2_queue *q) +{ + int ret; + struct phytium_jpeg_dev *jpeg_dev = vb2_get_drv_priv(q); + + clear_bit(VIDEO_STREAMING, &jpeg_dev->status); + ret = wait_event_timeout(jpeg_dev->wait, + !test_bit(VIDEO_FRAME_INPRG, &jpeg_dev->status), + STOP_TIMEOUT); + + /* time out */ + if (ret == 0) { + dev_err(jpeg_dev->dev, "Timed out when stopping streaming.\n"); + /* + * Need to force stop any DMA and try and get HW into a good states + * for future calls to start streaming again. + */ + phytium_jpeg_off(jpeg_dev); + phytium_jpeg_on(jpeg_dev); + phytium_jpeg_init_regs(jpeg_dev); + phytium_jpeg_get_resolution(jpeg_dev); + } + /* first stop jpeg, wait, the free buffer */ + phytium_jpeg_bufs_done(jpeg_dev, VB2_BUF_STATE_ERROR); +} + +static const struct vb2_ops phytium_jpeg_vb2_ops = { + .queue_setup = phytium_jpeg_queue_setup, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .buf_prepare = phytium_jpeg_buf_prepare, + .buf_queue = phytium_jpeg_buf_queue, + .start_streaming = phytium_jpeg_start_streaming, + .stop_streaming = phytium_jpeg_stop_streaming, +}; + +static void phytium_jpeg_set_yuv_mode(struct phytium_jpeg_dev *jpeg_dev) +{ + const char *mode = yuv_mode_str; + enum jpeg_yuv_mode yuv_mode; + + if (strstr(mode, "yuv422") != NULL) + yuv_mode = YUV422; + else if (strstr(mode, "yuv420") != NULL) + yuv_mode = YUV420; + else + yuv_mode = YUV444; + + /* set the yuv mode register */ + phytium_jpeg_write(jpeg_dev, SAMPLE_MODE_REG, yuv_mode); + + /* update the field which indicates YUV mode locates in the JPEG header. */ + phytium_jpeg_header[YUVID] &= 0xFFFF00FF; + if (yuv_mode == YUV422) + phytium_jpeg_header[YUVID] |= 0x2100; + else if (yuv_mode == YUV420) + phytium_jpeg_header[YUVID] |= 0x2200; + else + phytium_jpeg_header[YUVID] |= 0x1100; + +} + +static irqreturn_t phytium_jpeg_timer31_irq(int irq, void *arg) +{ + struct phytium_jpeg_dev *jpeg_dev = arg; + + /* disable timer interrupt */ + writel(0, jpeg_dev->timer31_addr); + + /* clear timer interrupt status */ + writel(0x8, jpeg_dev->timer31_addr + 0x2c); + + /* clear JPEG Engine's poweroff status */ + clear_bit(VIDEO_POWEROFF, &jpeg_dev->status); + dev_info(jpeg_dev->dev, "timer31 set jpeg status 0x%lx\n", jpeg_dev->status); + + /* JPEG Engine is poweron, reconfig quntization table and YUV mode */ + phytium_jpeg_init_jpeg_quant(jpeg_dev); + phytium_jpeg_set_yuv_mode(jpeg_dev); + phytium_jpeg_update(jpeg_dev, INT_STATUS_CTRL_REG, 0, DETECT_RESOLUTION_CHANGE_EN); + phytium_jpeg_update(jpeg_dev, TRANSFORM_INFO_REG, 0, TRANSINFO_SRC_SELECT); + + dev_info(jpeg_dev->dev, "reconfigure quant table and yuv mode\n"); + + return IRQ_HANDLED; +} + +static int phytium_jpeg_parser_timer31_irq(struct phytium_jpeg_dev *jpeg_dev) +{ + int irq; + int ret; + struct device *dev = jpeg_dev->dev; + + irq = irq_of_parse_and_map(dev->of_node, 2); + if (!irq) { + dev_err(dev, "Failed to get timer31 IRQ\n"); + return -ENODEV; + } + + ret = devm_request_threaded_irq(dev, irq, NULL, phytium_jpeg_timer31_irq, + IRQF_ONESHOT, PHYTIUM_JPEG_NAME, jpeg_dev); + if (ret < 0) + dev_err(dev, "Failed to request timer31 IRQ %d\n", irq); + + return ret; +} + +static irqreturn_t phytium_jpeg_timer30_irq(int irq, void *arg) +{ + struct phytium_jpeg_dev *jpeg_dev = arg; + struct arm_smccc_res res; + + /* disable timer interrupt */ + writel(0, jpeg_dev->timer30_addr); + /* clear timer interrupt status */ + writel(0x8, jpeg_dev->timer30_addr + 0x2c); + + /* Disable interruption */ + phytium_jpeg_update(jpeg_dev, INT_STATUS_CTRL_REG, STS_VE_JPEG_CODE_COMP_EN, 0); + + /* call SE to poweroff JPEG Engine */ + arm_smccc_smc(0xc300fff4, 0x9, 0x2, 0x80000020, 0, 0, 0, 0, &res); + + /* set JPEG Engine's status is poweroff */ + set_bit(VIDEO_POWEROFF, &jpeg_dev->status); + dev_info(jpeg_dev->dev, "timer30 set jpeg status 0x%lx\n", jpeg_dev->status); + + return IRQ_HANDLED; +} + +static int phytium_jpeg_parser_timer30_irq(struct phytium_jpeg_dev *jpeg_dev) +{ + int irq; + int ret; + struct device *dev = jpeg_dev->dev; + + irq = irq_of_parse_and_map(dev->of_node, 1); + if (!irq) { + dev_err(dev, "Failed to get timer30 IRQ\n"); + return -ENODEV; + } + + ret = devm_request_threaded_irq(dev, irq, NULL, phytium_jpeg_timer30_irq, + IRQF_ONESHOT, PHYTIUM_JPEG_NAME, jpeg_dev); + if (ret < 0) + dev_err(dev, "Failed to request timer30 IRQ %d\n", irq); + + return ret; +} + +static int phytium_jpeg_init(struct phytium_jpeg_dev *jpeg_dev) +{ + int irq; + int ret; + struct device *dev = jpeg_dev->dev; + u32 ocm_buf_addr[OCM_BUF_NUM]; + int i; + + irq = irq_of_parse_and_map(dev->of_node, 0); + if (!irq) { + dev_err(dev, "Failed to get IRQ\n"); + return -ENODEV; + } + + ret = devm_request_threaded_irq(dev, irq, NULL, phytium_jpeg_irq, + IRQF_ONESHOT, PHYTIUM_JPEG_NAME, jpeg_dev); + if (ret < 0) { + dev_err(dev, "Failed to request IRQ %d\n", irq); + return ret; + } + + ret = phytium_jpeg_parser_timer30_irq(jpeg_dev); + if (ret < 0) + return ret; + + ret = phytium_jpeg_parser_timer31_irq(jpeg_dev); + if (ret < 0) + return ret; + + ret = of_property_read_u32_array(dev->of_node, "phytium,ocm-buf-addr", + ocm_buf_addr, OCM_BUF_NUM); + if (ret != 0) { + dev_err(dev, "Failed to get the OCM address from device tree node.\n"); + return ret; + } + + for (i = 0; i < OCM_BUF_NUM; i++) + jpeg_dev->src_addrs[i].dma_addr = ocm_buf_addr[i]; + + /* CMA memory for JPEG device */ + of_reserved_mem_device_init(dev); + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); + if (ret != 0) { + dev_err(dev, "Failed to set DMA mask\n"); + return ret; + } + + /* Initializing JPEG Y and CbCr quantization table */ + phytium_jpeg_init_jpeg_quant(jpeg_dev); + + /* Select YUV mode */ + phytium_jpeg_set_yuv_mode(jpeg_dev); + dev_info(dev, "successfully initialize jpeg engine\n"); + return 0; + +} + + +static int phytium_jpeg_setup_video(struct phytium_jpeg_dev *jpeg_dev) +{ + struct v4l2_device *v4l2_dev = &jpeg_dev->v4l2_dev; + struct vb2_queue *dst_vq = &jpeg_dev->queue; + struct video_device *vdev = &jpeg_dev->vdev; + int ret; + + jpeg_dev->pix_fmt.pixelformat = V4L2_PIX_FMT_JPEG; + jpeg_dev->pix_fmt.field = V4L2_FIELD_NONE; + jpeg_dev->pix_fmt.colorspace = V4L2_COLORSPACE_SRGB; /* maybe ARGB */ + jpeg_dev->pix_fmt.quantization = V4L2_QUANTIZATION_FULL_RANGE; + jpeg_dev->v4l2_input_status = V4L2_IN_ST_NO_SIGNAL; + + ret = v4l2_device_register(jpeg_dev->dev, v4l2_dev); + if (ret != 0) { + dev_err(jpeg_dev->dev, "Failed to register v4l2 device\n"); + return ret; + } + + /* Register how many v4l2 controls to a handler */ + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + dst_vq->io_modes = VB2_MMAP | VB2_READ | VB2_DMABUF; + dst_vq->dev = v4l2_dev->dev; + dst_vq->lock = &jpeg_dev->video_lock; + dst_vq->ops = &phytium_jpeg_vb2_ops; + dst_vq->mem_ops = &vb2_dma_contig_memops; + dst_vq->drv_priv = jpeg_dev; + dst_vq->buf_struct_size = sizeof(struct phytium_jpeg_buffer); + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + dst_vq->min_buffers_needed = CAPTURE_BUF_NUMBER; + ret = vb2_queue_init(dst_vq); + if (ret) { + dev_err(jpeg_dev->dev, "Failed to init vb2 queue\n"); + goto err_v4l2_register; + } + + vdev->queue = dst_vq; + vdev->fops = &phytium_jpeg_fops; + vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; + vdev->v4l2_dev = v4l2_dev; + strscpy(vdev->name, PHYTIUM_JPEG_NAME, sizeof(vdev->name)); + vdev->vfl_type = VFL_TYPE_VIDEO; + vdev->vfl_dir = VFL_DIR_RX; + vdev->release = video_device_release_empty; + vdev->ioctl_ops = &phytium_jpeg_ioctl_ops; + vdev->lock = &jpeg_dev->video_lock; + + video_set_drvdata(vdev, jpeg_dev); + ret = video_register_device(vdev, VFL_TYPE_VIDEO, 0); + if (ret != 0) { + dev_err(jpeg_dev->dev, "Failed to register video device\n"); + goto err_video_register; + } + + v4l2_info(v4l2_dev, "phytium JPEG registered as /dev/video%d (%d, %d)\n", + jpeg_dev->vdev.num, VIDEO_MAJOR, jpeg_dev->vdev.minor); + return ret; + +err_video_register: + vb2_queue_release(dst_vq); + +err_v4l2_register: + v4l2_device_unregister(v4l2_dev); + return ret; +} + +static const struct phytium_jpeg_config phytium_jpeg_config = { + .comp_size_read = BUF_LIST_INDEX_CTRL_STS_ADDR(VB_BUF_NO), +}; + +static const struct of_device_id phytium_jpeg_match[] = { + { + .compatible = "phytium,jpeg", + .data = &phytium_jpeg_config, + }, + {}, +}; + +MODULE_DEVICE_TABLE(of, phytium_jpeg_match); + +static int phytium_jpeg_probe(struct platform_device *pdev) +{ + struct phytium_jpeg_dev *jpeg_dev; + const struct of_device_id *match; + const struct phytium_jpeg_config *config; + struct resource *res; + int ret; + + jpeg_dev = devm_kzalloc(&pdev->dev, sizeof(*jpeg_dev), GFP_KERNEL); + if (jpeg_dev == NULL) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + jpeg_dev->base_addr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(jpeg_dev->base_addr)) { + dev_err(jpeg_dev->dev, "Failed to ioremap.\n"); + return PTR_ERR(jpeg_dev->base_addr); + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + jpeg_dev->timer30_addr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(jpeg_dev->base_addr)) { + dev_err(jpeg_dev->dev, "Failed to parse timer30.\n"); + return PTR_ERR(jpeg_dev->timer30_addr); + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 2); + jpeg_dev->timer31_addr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(jpeg_dev->base_addr)) { + dev_err(jpeg_dev->dev, "Failed to parse timer30.\n"); + return PTR_ERR(jpeg_dev->timer31_addr); + } + match = of_match_node(phytium_jpeg_match, pdev->dev.of_node); + if (match == NULL) { + dev_err(jpeg_dev->dev, "Failed to match.\n"); + return -EINVAL; + } + + config = match->data; + jpeg_dev->comp_size_read = config->comp_size_read; + + jpeg_dev->frame_rate = 30; + jpeg_dev->dev = &pdev->dev; + spin_lock_init(&jpeg_dev->hw_lock); + mutex_init(&jpeg_dev->video_lock); + init_waitqueue_head(&jpeg_dev->wait); + INIT_DELAYED_WORK(&jpeg_dev->res_work, phytium_jpeg_resolution_work); + INIT_LIST_HEAD(&jpeg_dev->buffers); + + ret = phytium_jpeg_init(jpeg_dev); + if (ret != 0) { + dev_err(jpeg_dev->dev, "Failed to initialize the JPEG engine.\n"); + return ret; + } + + ret = phytium_jpeg_setup_video(jpeg_dev); + + return ret; +} + +#define to_phytium_jpeg(x) container_of((x), struct phytium_jpeg_dev, v4l2_dev) +static int phytium_jpeg_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); + struct phytium_jpeg_dev *jpeg_dev = to_phytium_jpeg(v4l2_dev); + + phytium_jpeg_off(jpeg_dev); + + video_unregister_device(&jpeg_dev->vdev); + + vb2_queue_release(&jpeg_dev->queue); + + v4l2_device_unregister(v4l2_dev); + + of_reserved_mem_device_release(dev); + + return 0; +} + +static struct platform_driver phytium_jpeg_driver = { + .probe = phytium_jpeg_probe, + .remove = phytium_jpeg_remove, + .driver = { + .name = PHYTIUM_JPEG_NAME, + .of_match_table = phytium_jpeg_match, + }, +}; + +module_platform_driver(phytium_jpeg_driver); + +MODULE_DESCRIPTION("Phytium JPEG Encoder driver"); +MODULE_AUTHOR("Wang Min "); +MODULE_LICENSE("GPL v2"); diff --git a/target/linux/phytium/files-5.10/drivers/media/platform/phytium-jpeg/phytium_jpeg_core.h b/target/linux/phytium/files-5.10/drivers/media/platform/phytium-jpeg/phytium_jpeg_core.h new file mode 100644 index 000000000..1c2b55b65 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/media/platform/phytium-jpeg/phytium_jpeg_core.h @@ -0,0 +1,147 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#ifndef _PHYTIUM_JPEG_CORE_H +#define _PHYTIUM_JPEG_CORE_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PHYTIUM_JPEG_NAME "phytium-jpeg" +#define MAX_FRAME_RATE 60 +#define MAX_HEIGHT 1080 +#define MAX_WIDTH 1920 +#define MIN_HEIGHT 480 +#define MIN_WIDTH 640 +#define MIN_PIXEL_CLOCK (640 * 480 * 60) /* 640 x 480 x 60Hz */ +#define MAX_PIXEL_CLOCK (1920 * 1080 * 60) /* 1920 x 1080 x 60Hz */ + +#define SOURCE_RESOLUTION_DETECT_TIMEOUT msecs_to_jiffies(500) +#define RESOLUTION_CHANGE_DELAY msecs_to_jiffies(0) +#define INVALID_RESOLUTION_DELAY msecs_to_jiffies(250) +#define STOP_TIMEOUT msecs_to_jiffies(1000) + +#define INVALID_RESOLUTION_RETRIES 2 +#define CAPTURE_BUF_NUMBER 3 /* using how many buffers */ +#define VB_BUF_NO 0 /* there are 16 buffer, use which one */ + +/* The below macros are defined for the JPEG header of the phytium JPEG Engine */ +#define PHYTIUM_JPEG_HEADER_LEN (256 * 3) +#define PHYTIUM_JPEG_HEADER_SIZE (PHYTIUM_JPEG_HEADER_LEN / sizeof(u32)) +#define PHYTIUM_JPEG_HEADER_H_INDEX 40 +#define PHYTIUM_JPEG_HEADER_W_INDEX 41 + +/* There are two ocm buffers that are used for storaging the inputing video data */ +#define OCM_BUF_NUM 2 + +enum phytium_jpeg_status { + VIDEO_MODE_DETECT_DONE, + VIDEO_RES_CHANGE, + VIDEO_RES_DETECT, + VIDEO_STREAMING, + VIDEO_FRAME_INPRG, + VIDEO_STOPPED, + VIDEO_CLOCKS_ON, + VIDEO_POWEROFF, +}; + +struct phytium_jpeg_addr { + unsigned int size; + dma_addr_t dma_addr; + void *virt_addr; +}; + +struct phytium_jpeg_buffer { + struct vb2_v4l2_buffer vb; + struct list_head link; +}; + +/** + * struct phytium_jpeg - JPEG IP abstraction + * @lock: the mutex protecting this structure + * @hw_lock: spinlock protecting the hw device resource + * @workqueue: decode work queue + * @dev: JPEG device + * @v4l2_dev: v4l2 device for mem2mem mode + * @m2m_dev: v4l2 mem2mem device data + * @alloc_ctx: videobuf2 memory allocator's context + * @dec_vdev: video device node for decoder mem2mem mode + * @dec_reg_base: JPEG registers mapping + * @clk_jdec: JPEG hw working clock + * @clk_jdec_smi: JPEG SMI bus clock + * @larb: SMI device + */ +struct phytium_jpeg_dev { + void __iomem *base_addr; + struct device *dev; + struct v4l2_device v4l2_dev; + struct v4l2_pix_format pix_fmt; + struct v4l2_bt_timings active_timings; + struct v4l2_bt_timings detected_timings; + u32 v4l2_input_status; + struct vb2_queue queue; + struct video_device vdev; + /* v4l2 and videobuf2 lock, protect the structure*/ + struct mutex video_lock; + u32 jpeg_mode; + u32 comp_size_read; + wait_queue_head_t wait; + /* buffer list lock, protecting the hw device resource */ + spinlock_t hw_lock; + struct delayed_work res_work; + struct list_head buffers; + unsigned long status; + unsigned int sequence; + unsigned int max_compressed_size; + struct phytium_jpeg_addr src_addrs[OCM_BUF_NUM]; + struct phytium_jpeg_addr dst_addrs[16]; + + bool yuv420; + unsigned int frame_rate; + void __iomem *timer30_addr; + void __iomem *timer31_addr; +}; + +struct phytium_jpeg_config { + u32 jpeg_mode; + u32 comp_size_read; +}; + +#define YUV_MODE_STR_LEN 8 +#define YUVID 42 + +enum jpeg_yuv_mode { + YUV444 = 0x0, + YUV422 = 0x1, + YUV420 = 0x2 +}; + +#endif /* _PHYTIUM_JPEG_CORE_H */ diff --git a/target/linux/phytium/files-5.10/drivers/media/platform/phytium-jpeg/phytium_jpeg_reg.h b/target/linux/phytium/files-5.10/drivers/media/platform/phytium-jpeg/phytium_jpeg_reg.h new file mode 100644 index 000000000..3567badf9 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/media/platform/phytium-jpeg/phytium_jpeg_reg.h @@ -0,0 +1,113 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#ifndef _PHYTIUM_JPEG_REG_H +#define _PHYTIUM_JPEG_REG_H + +#include +/* define the all kinds of control registers in a JPEG soc */ + +/* The register is used to set the information of the video comes from main memory */ +#define SRC_DDR_INFO_REG 0x00000800 + +/* The register is used to get the information of the video comes from external VGA */ +#define SRC_VGA_INFO_REG 0x00000894 + +#define SRC_FORMAT BIT(0) /* 0:RGB888, 1:RGB565 */ +#define SRC_DE_POLARITY BIT(1) /* 0:low level is effect, other */ +#define SRC_HS_POLARITY BIT(2) /* 0:low level is effect, other */ +#define SRC_VS_POLARITY BIT(3) /* 0:low level is effect, other */ +#define SRC_HOR_PIXELS GENMASK(15, 4) /* the number of the horizontal pixels */ +#define SRC_WIDTH_SHIFT 4 /* shift right to get width */ +#define SRC_VER_PIXELS GENMASK(26, 16) /* the number of the vertical pixels */ +#define SRC_HEIGHT_SHIFT 16 /* shift right to get height */ +/* The below bit fields is only used by image comes from main memory */ +#define SRC_COMP_DDR_IMG_EN BIT(27) /* 0: disable to JPEG compression, others */ + +/* marks which ocm buffer is occupied to store an image */ +#define SRC_DDR_IMG2OCM_VALID GENMASK(29, 28) + +/* The register controls starting work of the JPEG engine */ +#define TRANSFORM_INFO_REG 0x00000804 +#define TRANSINFO_ENABLE_ENGINE BIT(0) /* 1: enable the JPEG engine */ +/* 1: video comes from external VGA, 0: video comes from DDR */ +#define TRANSINFO_SRC_SELECT BIT(1) +/* 0: video comes from external VGA is cached to OCM, 1: DDR */ +#define TRANSINFO_IMAGE_STORE BIT(2) +#define TRANSINFO_FRAME_RATE GENMASK(9, 4) /* the value notes frame rate */ +#define TRANSINFO_BLOCK_SIZE BIT(12) /* 0: 8x8, 1: 16x16 */ +#define TRANSINFO_ENABLE_YUV422 BIT(13) /* 1: JPEG block is populated YUV422 */ +/* support burst with the values such as 1, 2, 4, 8, 16, 32, 64. using default value 0xf */ +#define TRANSINFO_AXI_LEN GENMASK(22, 16) +#define TRANS_AXI_LEN_SHIFT 16 + +/* The interrupt and status register */ +#define INT_STATUS_CTRL_REG 0x00000808 +#define INT_FIFO_OVERFLOW BIT(0) /* video fifo overflow, write 1 to clear */ +#define INT_OCM_BUF_OVERFLOW BIT(1) /* ocm buffer overflow, write 1 to clear */ +/* JPEG engine complete compression, write 1 to clear */ +#define INT_JPEG_ENCODE_COMPLETE BIT(2) +/* in VGA mode, video's format is changed */ +#define INT_VIDEO_FORMAT_CHANGE BIT(3) +/* enable the interrupt of th video fifo overflow and source resolution */ +#define DETECT_RESOLUTION_CHANGE_EN BIT(4) +/* enable the interrupt of the ocm buffer overflow */ +#define STS_VE_OCM_BUF_OVERFLOW_EN BIT(5) +/* enable the interrupt of the JPEG complete compression */ +#define STS_VE_JPEG_CODE_COMP_EN BIT(6) +/* in VGA mode, the bit notes ocm buff is busy */ +#define STS_VE_OCM_BUF_BUSY BIT(7) +/* in VGA mode, the bit notes sequence number of the frame */ +#define STS_VE_CUR_FRAME_NUMBER GENMASK(9, 8) +/* in VGA mode, the bit notes sequence number of the cached frame */ +#define STS_VE_BUF_CACHE_NUMBER GENMASK(11, 10) +/* in VGA mode, the buffer number in buffer list */ +#define STS_JPEG_COMP_BUF_NO GENMASK(15, 12) +#define INT_JPEG_COMP_BUF_LIST_NO GENMASK(31, 16) /* the interrupt number of the buffer */ + +#define OCM_BUF0_ADDR 0x0000080C +#define OCM_BUF1_ADDR 0x00000810 +#define OCM_BUF_SHIFT 8 + +#define BUF_LIST_BASE_ADDR 0x00000814 + +#define PHYTIUM_BUF_LIST_ACTRL_AND_STS_BASE_ADDR_REG 0x00000818 +#define STS_JPEG_BUF_HIGH_LEVEL_VALID BIT(0) /* Hight levle is validity */ +#define JPEG_BUF_CAPACITY_SIZE GENMASK(29, 8) /* the capacity of the buffer */ +#define JPEG_BUF_CAPACITY_SIZE_SHIFT 8 + +/* There are 16 buffers in the buffer list, the width between each other' address is 8 bytes */ +#define BUF_LIST_ADDR_OFFSET 0x8 +#define BUF_LIST_CTRL_AND_STS_OFFSET 0x8 + +/* Get the address of the specific index buffer */ +#define BUF_LIST_INDEX_ADDR(index) \ + (BUF_LIST_BASE_ADDR + (index) * BUF_LIST_ADDR_OFFSET) + +#define JPEG_DST_ADDR_SHIFT 8 + +#define BUF_LIST_INDEX_CTRL_STS_ADDR(index) \ + (PHYTIUM_BUF_LIST_ACTRL_AND_STS_BASE_ADDR_REG + (index) * BUF_LIST_CTRL_AND_STS_OFFSET) + +#define FRAME_SAMPLE_CTRL 0x00000898 +#define FRAME_SAMPLE_CTRL_EN BIT(31) +#define FRAME_SAMPLE_INTERVAL GENMASK(30, 0) + +/* The below registers are all related to quantilize */ +#define HUFF_MODE_REG 0x300 +#define SAMPLE_MODE_REG 0x304 + +#define Y_QUANT_BASE_ADDR_REG 0x400 +#define C_QUANT_BASE_ADDR_REG 0x500 + +#define QUANT_REG_NUM 64 + +#define Y_QUANT_INDEX_ADDR_REG(index) \ + (Y_QUANT_BASE_ADDR_REG + 4 * (index)) + +#define C_QUANT_INDEX_ADDR_REG(index) \ + (C_QUANT_BASE_ADDR_REG + 4 * (index)) + +#endif /* _PHYTIUM_JPEG_REG_H */ diff --git a/target/linux/phytium/files-5.10/drivers/mfd/phytium_px210_i2s_lsd.c b/target/linux/phytium/files-5.10/drivers/mfd/phytium_px210_i2s_lsd.c new file mode 100644 index 000000000..a76b5d770 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/mfd/phytium_px210_i2s_lsd.c @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Phytium I2S LSD MFD driver over PCI bus + * + * Copyright (c) 2020-2023 Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include + +struct phytium_px210_mfd { + struct device *dev; +}; + +struct pdata_px210_mfd { + struct device *dev; + char *name; + int clk_base; +}; + +static struct resource phytium_px210_i2s_res0[] = { + [0] = { + .flags = IORESOURCE_MEM, + }, + [1] = { + .flags = IORESOURCE_MEM, + }, + [2] = { + .flags = IORESOURCE_IRQ, + }, +}; + +static struct mfd_cell phytium_px210_mfd_cells[] = { + { + .id = 0, + .name = "phytium-i2s", + .of_compatible = "phytium,i2s", + .resources = phytium_px210_i2s_res0, + .num_resources = ARRAY_SIZE(phytium_px210_i2s_res0), + .ignore_resource_conflicts = true, + }, +}; + +static void phytium_px210_i2s_setup(struct pci_dev *pdev) +{ + struct mfd_cell *cell = &phytium_px210_mfd_cells[0]; + struct resource *res = (struct resource *)cell->resources; + struct pdata_px210_mfd *pdata; + + res[0].start = pci_resource_start(pdev, 0); + res[0].end = pci_resource_start(pdev, 0) + 0x0fff; + + res[1].start = pci_resource_start(pdev, 0) + 0x1000; + res[1].end = pci_resource_start(pdev, 0) + 0x1fff; + + res[2].start = pdev->irq; + res[2].end = pdev->irq; + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + + pdata->dev = &pdev->dev; + pdata->name = "phytium-i2s-lsd"; + pdata->clk_base = 480000000; + + cell->platform_data = pdata; + cell->pdata_size = sizeof(*pdata); +} + +static int phytium_px210_mfd_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct phytium_px210_mfd *phytium_mfd; + int ret; + + ret = pcim_enable_device(pdev); + if (ret) + return ret; + + pci_set_master(pdev); + + phytium_mfd = devm_kzalloc(&pdev->dev, sizeof(*phytium_mfd), GFP_KERNEL); + if (!phytium_mfd) + return -ENOMEM; + + phytium_mfd->dev = &pdev->dev; + dev_set_drvdata(&pdev->dev, phytium_mfd); + + phytium_px210_i2s_setup(pdev); + + ret = mfd_add_devices(&pdev->dev, 0, phytium_px210_mfd_cells, + ARRAY_SIZE(phytium_px210_mfd_cells), NULL, 0, + NULL); + if (ret) + return 0; + + return 0; +} + + +static void phytium_px210_mfd_remove(struct pci_dev *pdev) +{ + mfd_remove_devices(&pdev->dev); +} + +static const struct pci_device_id phytium_px210_mfd_ids[] = { + { + .vendor = 0x1DB7, + .device = 0xDC2B, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .class = 0x3, + .class_mask = 0, + }, + {}, +}; +MODULE_DEVICE_TABLE(pci, phytium_px210_mfd_ids); + +static struct pci_driver phytium_i2s_lsd_mfd_driver = { + .name = "phytium_px210_mfd_i2s", + .id_table = phytium_px210_mfd_ids, + .probe = phytium_px210_mfd_probe, + .remove = phytium_px210_mfd_remove, +}; + +module_pci_driver(phytium_i2s_lsd_mfd_driver); + +MODULE_AUTHOR("Yiqun Zhang "); +MODULE_DESCRIPTION("Phytium Px210 MFD PCI driver for I2S-LSD"); +MODULE_LICENSE("GPL v2"); diff --git a/target/linux/phytium/files-5.10/drivers/mfd/phytium_px210_i2s_mmd.c b/target/linux/phytium/files-5.10/drivers/mfd/phytium_px210_i2s_mmd.c new file mode 100644 index 000000000..4020686d4 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/mfd/phytium_px210_i2s_mmd.c @@ -0,0 +1,185 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Phytium I2S MMD MFD driver over PCI bus + * + * Copyright (c) 2020-2023 Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include + +struct phytium_px210_mfd { + struct device *dev; +}; + +struct pdata_px210_mfd { + struct device *dev; + char *name; + int clk_base; +}; + +static struct resource phytium_px210_i2s_res0[] = { + [0] = { + .flags = IORESOURCE_MEM, + }, + [1] = { + .flags = IORESOURCE_MEM, + }, + [2] = { + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource phytium_px210_i2s_res1[] = { + [0] = { + .flags = IORESOURCE_MEM, + }, + [1] = { + .flags = IORESOURCE_MEM, + }, + [2] = { + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource phytium_px210_i2s_res2[] = { + [0] = { + .flags = IORESOURCE_MEM, + }, + [1] = { + .flags = IORESOURCE_MEM, + }, + [2] = { + .flags = IORESOURCE_IRQ, + }, +}; + +static struct mfd_cell phytium_px210_mfd_cells[] = { + { + .id = 1, + .name = "phytium-i2s", + .of_compatible = "phytium,i2s", + .resources = phytium_px210_i2s_res0, + .num_resources = ARRAY_SIZE(phytium_px210_i2s_res0), + .ignore_resource_conflicts = true, + }, + { + .id = 2, + .name = "phytium-i2s", + .of_compatible = "phytium,i2s", + .resources = phytium_px210_i2s_res1, + .num_resources = ARRAY_SIZE(phytium_px210_i2s_res1), + .ignore_resource_conflicts = true, + }, + { + .id = 3, + .name = "phytium-i2s", + .of_compatible = "phytium,i2s", + .resources = phytium_px210_i2s_res2, + .num_resources = ARRAY_SIZE(phytium_px210_i2s_res2), + .ignore_resource_conflicts = true, + }, +}; + +static void phytium_px210_i2s_setup(struct pci_dev *pdev, int i) +{ + struct mfd_cell *cell = &phytium_px210_mfd_cells[i]; + struct resource *res = (struct resource *)cell->resources; + struct pdata_px210_mfd *pdata; + + res[0].start = pci_resource_start(pdev, 0) + 0x2000 * i + 0x1000; + res[0].end = pci_resource_start(pdev, 0) + 0x2000 * i + 0x1fff; + + res[1].start = pci_resource_start(pdev, 0) + 0x2000 * i; + res[1].end = pci_resource_start(pdev, 0) + 0x2000 * i + 0x0fff; + + res[2].start = pdev->irq; + res[2].end = pdev->irq; + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + + pdata->dev = &pdev->dev; + pdata->clk_base = 600000000; + switch (i) { + case 0: + pdata->name = "phytium-i2s-dp0"; + break; + case 1: + pdata->name = "phytium-i2s-dp1"; + break; + case 2: + pdata->name = "phytium-i2s-dp2"; + break; + default: + break; + } + + cell->platform_data = pdata; + cell->pdata_size = sizeof(*pdata); +} + +static int phytium_px210_mfd_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct phytium_px210_mfd *phytium_mfd; + int i; + int ret; + + ret = pcim_enable_device(pdev); + if (ret) + return ret; + + pci_set_master(pdev); + + phytium_mfd = devm_kzalloc(&pdev->dev, sizeof(*phytium_mfd), GFP_KERNEL); + if (!phytium_mfd) + return -ENOMEM; + + phytium_mfd->dev = &pdev->dev; + dev_set_drvdata(&pdev->dev, phytium_mfd); + + for (i = 0; i < 3; i++) + phytium_px210_i2s_setup(pdev, i); + + ret = mfd_add_devices(&pdev->dev, 0, phytium_px210_mfd_cells, + ARRAY_SIZE(phytium_px210_mfd_cells), NULL, 0, + NULL); + if (ret) + return 0; + + return 0; +} + + +static void phytium_px210_mfd_remove(struct pci_dev *pdev) +{ + mfd_remove_devices(&pdev->dev); +} + +static const struct pci_device_id phytium_px210_mfd_ids[] = { + { + .vendor = 0x1DB7, + .device = 0xDC23, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .class = 0x3, + .class_mask = 0, + }, + {}, +}; +MODULE_DEVICE_TABLE(pci, phytium_px210_mfd_ids); + +static struct pci_driver phytium_i2s_mmd_mfd_driver = { + .name = "phytium_px210_mfd_mmd", + .id_table = phytium_px210_mfd_ids, + .probe = phytium_px210_mfd_probe, + .remove = phytium_px210_mfd_remove, +}; + +module_pci_driver(phytium_i2s_mmd_mfd_driver); + +MODULE_AUTHOR("Yiqun Zhang "); +MODULE_DESCRIPTION("Phytium Px210 MFD PCI driver for I2S-DP"); +MODULE_LICENSE("GPL v2"); diff --git a/target/linux/phytium/files-5.10/drivers/mmc/host/phytium-mci-pci.c b/target/linux/phytium/files-5.10/drivers/mmc/host/phytium-mci-pci.c new file mode 100644 index 000000000..b397a2570 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/mmc/host/phytium-mci-pci.c @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Phytium Multimedia Card Interface PCI driver + * + * Copyright (c) 2020-2023 Phytium Technology Co., Ltd. + * + */ + +#include +#include +#include +#include +#include +#include +#include "phytium-mci.h" + +static u32 sd_caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_CMD23 | + MMC_CAP_4_BIT_DATA; +static u32 sd_caps2 = MMC_CAP2_NO_MMC; + +static u32 emmc_caps = MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA | MMC_CAP_WAIT_WHILE_BUSY | + MMC_CAP_CMD23 | MMC_CAP_HW_RESET | MMC_CAP_MMC_HIGHSPEED | + MMC_CAP_NONREMOVABLE; +static u32 emmc_caps2 = MMC_CAP2_NO_SDIO | MMC_CAP2_NO_SD; + +#define PCI_BAR_NO 0 + +#if defined CONFIG_PM && defined CONFIG_PM_SLEEP +static const struct dev_pm_ops phytium_mci_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(phytium_mci_suspend, + phytium_mci_resume) + SET_RUNTIME_PM_OPS(phytium_mci_runtime_suspend, + phytium_mci_runtime_resume, NULL) +}; +#else +#define phytium_mci_dev_pm_ops NULL +#endif + +static int +phytium_mci_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pid) +{ + struct phytium_mci_host *host; + struct mmc_host *mmc; + int ret; + + ret = pcim_enable_device(pdev); + + if (ret) + return ret; + pci_set_master(pdev); + + mmc = mmc_alloc_host(sizeof(struct phytium_mci_host), &pdev->dev); + + if (!mmc) + return -ENOMEM; + + host = mmc_priv(mmc); + + pci_enable_msi(pdev); + + host->irq = pdev->irq; + host->irq_flags = IRQF_SHARED; + host->dev = &pdev->dev; + ret = pcim_iomap_regions(pdev, 1 << PCI_BAR_NO, pci_name(pdev)); + + if (ret) { + dev_err(&pdev->dev, "I/O memory remapping failed\n"); + goto host_free; + } + + host->base = pcim_iomap_table(pdev)[PCI_BAR_NO]; + host->is_use_dma = 1; + host->is_device_x100 = 1; + + if (pdev->devfn == 2) { + host->caps = emmc_caps; + host->caps2 = emmc_caps2; + } else { + host->caps = sd_caps; + host->caps2 = sd_caps2; + mmc->f_max = 25000000; /* stable frequency */ + } + + host->mmc = mmc; + host->clk_rate = MCI_CLK; + + dev_info(&pdev->dev, "%s %d: [bar %d] addr: 0x%llx size: 0x%llx km: 0x%llx devfn:%d\n", + __func__, __LINE__, PCI_BAR_NO, pci_resource_start(pdev, 0), + pci_resource_len(pdev, 0), (uint64_t)host->base, pdev->devfn); + + dev_dbg(&pdev->dev, "%s %d:irq:0x%x\n", __func__, __LINE__, host->irq); + + ret = phytium_mci_common_probe(host); + + if (ret == MCI_REALEASE_MEM) { + ret = -ENOMEM; + goto release_mem; + } else if (ret) { + goto release; + } + pci_set_drvdata(pdev, mmc); + dev_info(&pdev->dev, "%s %d: probe phytium mci successful.\n", __func__, __LINE__); + return 0; + +release: + phytium_mci_deinit_hw(host); +release_mem: + + if (host->dma.adma_table) { + dma_free_coherent(&pdev->dev, + MAX_BD_NUM * sizeof(struct phytium_adma2_64_desc), + host->dma.adma_table, host->dma.adma_addr); + } +host_free: + mmc_free_host(mmc); + pci_disable_device(pdev); + return ret; +} + +static void phytium_mci_pci_remove(struct pci_dev *pdev) +{ + struct phytium_mci_host *host; + struct mmc_host *mmc; + + mmc = pci_get_drvdata(pdev); + if (!mmc) { + dev_info(&pdev->dev, "%s %d: mmc is null.\n", __func__, __LINE__); + return; + } + host = mmc_priv(mmc); + if (!host) { + dev_info(&pdev->dev, "%s %d: host is null.\n", __func__, __LINE__); + mmc_remove_host(mmc); + mmc_free_host(mmc); + return; + } + + del_timer(&host->hotplug_timer); + + mmc_remove_host(host->mmc); + + if (host->dma.adma_table) { + dma_free_coherent(&pdev->dev, + MAX_BD_NUM * sizeof(struct phytium_adma2_64_desc), + host->dma.adma_table, host->dma.adma_addr); + } + phytium_mci_deinit_hw(host); + mmc_free_host(mmc); + pci_set_drvdata(pdev, NULL); +} + +static const struct pci_device_id phytium_mci_pci_tbl[] = { + { + PCI_DEVICE(PCI_VENDOR_ID_PHYTIUM, 0xdc28), + .class = 0x5, + .class_mask = 0, + }, + {} +}; +MODULE_DEVICE_TABLE(pci, phytium_mci_pci_tbl); + +static struct pci_driver phytium_mci_pci_driver = { + .name = "phytium-mci-pci", + .id_table = phytium_mci_pci_tbl, + .probe = phytium_mci_pci_probe, + .remove = phytium_mci_pci_remove, + .driver = { + .pm = &phytium_mci_dev_pm_ops, + } +}; +module_pci_driver(phytium_mci_pci_driver); + +MODULE_DESCRIPTION("Phytium Multimedia Card Interface PCI driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Cheng Quan "); diff --git a/target/linux/phytium/files-5.10/drivers/mmc/host/phytium-mci-plat.c b/target/linux/phytium/files-5.10/drivers/mmc/host/phytium-mci-plat.c new file mode 100644 index 000000000..d4d51adbf --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/mmc/host/phytium-mci-plat.c @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Phytium Multimedia Card Interface PCI driver + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "phytium-mci.h" + +static u32 mci_caps = MMC_CAP_CMD23 | MMC_CAP_WAIT_WHILE_BUSY; + +#if defined CONFIG_PM && defined CONFIG_PM_SLEEP + +static const struct dev_pm_ops phytium_mci_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(phytium_mci_suspend, + phytium_mci_resume) + SET_RUNTIME_PM_OPS(phytium_mci_runtime_suspend, + phytium_mci_runtime_resume, NULL) +}; +#else +#define phytium_mci_dev_pm_ops NULL +#endif + +static int phytium_mci_probe(struct platform_device *pdev) +{ + struct mmc_host *mmc; + struct phytium_mci_host *host; + struct resource *res; + const struct acpi_device_id *match; + struct device *dev = &pdev->dev; + int ret; + + mmc = mmc_alloc_host(sizeof(struct phytium_mci_host), &pdev->dev); + if (!mmc) + return -ENOMEM; + host = mmc_priv(mmc); + ret = mmc_of_parse(mmc); + if (ret) + goto host_free; + + if (dev->of_node) { + host->src_clk = devm_clk_get(&pdev->dev, "phytium_mci_clk"); + if (IS_ERR(host->src_clk)) { + ret = PTR_ERR(host->src_clk); + goto host_free; + } + + host->clk_rate = clk_get_rate(host->src_clk); + } else if (has_acpi_companion(dev)) { + match = acpi_match_device(dev->driver->acpi_match_table, dev); + if (!match) { + dev_err(dev, "Error ACPI match data is missing\n"); + return -ENODEV; + } + host->clk_rate = 1200000000; + } + + host->is_use_dma = 1; + host->is_device_x100 = 0; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + host->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(host->base)) { + ret = PTR_ERR(host->base); + goto host_free; + } + + host->irq = platform_get_irq(pdev, 0); + + if (host->irq < 0) { + ret = -EINVAL; + goto host_free; + } + host->irq_flags = IRQF_SHARED; + dev_dbg(&pdev->dev, "%s %d:irq:%d\n", __func__, __LINE__, host->irq); + host->dev = &pdev->dev; + host->caps = mci_caps; + host->mmc = mmc; + ret = phytium_mci_common_probe(host); + if (ret == MCI_REALEASE_MEM) { + ret = -ENOMEM; + goto release_mem; + } else if (ret) { + goto release; + } + platform_set_drvdata(pdev, mmc); + dev_info(&pdev->dev, "%s %d: probe phytium mci successful.\n", __func__, __LINE__); + return 0; + +release: + phytium_mci_deinit_hw(host); +release_mem: + if (host->dma.adma_table) { + dma_free_coherent(&pdev->dev, + MAX_BD_NUM * sizeof(struct phytium_adma2_64_desc), + host->dma.adma_table, host->dma.adma_addr); + } +host_free: + mmc_free_host(mmc); + return ret; +} + +static int phytium_mci_remove(struct platform_device *pdev) +{ + struct mmc_host *mmc; + struct phytium_mci_host *host; + + mmc = platform_get_drvdata(pdev); + if (!mmc) { + dev_info(&pdev->dev, "%s %d: mmc is null.\n", __func__, __LINE__); + return -1; + } + host = mmc_priv(mmc); + if (!host) { + dev_info(&pdev->dev, "%s %d: host is null.\n", __func__, __LINE__); + mmc_remove_host(mmc); + mmc_free_host(mmc); + return -1; + } + del_timer(&host->hotplug_timer); + del_timer_sync(&host->timeout_timer); + mmc_remove_host(host->mmc); + + if (host->dma.adma_table) { + dma_free_coherent(&pdev->dev, + MAX_BD_NUM * sizeof(struct phytium_adma2_64_desc), + host->dma.adma_table, host->dma.adma_addr); + } + phytium_mci_deinit_hw(host); + mmc_free_host(mmc); + platform_set_drvdata(pdev, NULL); + return 0; +} + +static const struct of_device_id phytium_mci_of_ids[] = { + { .compatible = "phytium,mci", }, + {} +}; + +MODULE_DEVICE_TABLE(of, phytium_mci_of_ids); + +#ifdef CONFIG_ACPI +static const struct acpi_device_id phytium_mci_acpi_ids[] = { + { .id = "PHYT0017" }, + { } +}; + +MODULE_DEVICE_TABLE(acpi, phytium_mci_acpi_ids); +#else +#define phytium_mci_acpi_ids NULL +#endif + +static struct platform_driver phytium_mci_driver = { + .probe = phytium_mci_probe, + .remove = phytium_mci_remove, + .driver = { + .name = "phytium-mci-platform", + .of_match_table = phytium_mci_of_ids, + .acpi_match_table = phytium_mci_acpi_ids, + .pm = &phytium_mci_dev_pm_ops, + }, +}; + +module_platform_driver(phytium_mci_driver); + +MODULE_DESCRIPTION("Phytium Multimedia Card Interface driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Cheng Quan "); diff --git a/target/linux/phytium/files-5.10/drivers/mmc/host/phytium-mci.c b/target/linux/phytium/files-5.10/drivers/mmc/host/phytium-mci.c new file mode 100644 index 000000000..22ec14694 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/mmc/host/phytium-mci.c @@ -0,0 +1,1564 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Driver for Phytium Multimedia Card Interface + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "phytium-mci.h" + +static const u32 cmd_ints_mask = MCI_INT_MASK_RE | MCI_INT_MASK_CMD | MCI_INT_MASK_RCRC | + MCI_INT_MASK_RTO | MCI_INT_MASK_HTO | MCI_RAW_INTS_HLE; + +static const u32 data_ints_mask = MCI_INT_MASK_DTO | MCI_INT_MASK_DCRC | MCI_INT_MASK_DRTO | + MCI_INT_MASK_SBE_BCI; +static const u32 cmd_err_ints_mask = MCI_INT_MASK_RTO | MCI_INT_MASK_RCRC | MCI_INT_MASK_RE | + MCI_INT_MASK_DCRC | MCI_INT_MASK_DRTO | + MCI_MASKED_INTS_SBE_BCI; + +static const u32 dmac_ints_mask = MCI_DMAC_INT_ENA_FBE | MCI_DMAC_INT_ENA_DU | + MCI_DMAC_INT_ENA_NIS | MCI_DMAC_INT_ENA_AIS; +static const u32 dmac_err_ints_mask = MCI_DMAC_INT_ENA_FBE | MCI_DMAC_INT_ENA_DU | + MCI_DMAC_INT_ENA_AIS; + +static void phytium_mci_cmd_next(struct phytium_mci_host *host, + struct mmc_request *mrq, + struct mmc_command *cmd); +static void phytium_mci_adma_reset(struct phytium_mci_host *host); +static void phytium_mci_send_cmd(struct phytium_mci_host *host, u32 cmd, u32 arg); +static bool phytium_mci_data_xfer_done(struct phytium_mci_host *host, u32 events, + struct mmc_request *mrq, struct mmc_data *data); +static void phytium_mci_init_adma_table(struct phytium_mci_host *host, + struct phytium_mci_dma *dma); +static void phytium_mci_init_hw(struct phytium_mci_host *host); +static int phytium_mci_get_cd(struct mmc_host *mmc); +static int phytium_mci_err_irq(struct phytium_mci_host *host, u32 dmac_events, u32 events); + +static void sdr_set_bits(void __iomem *reg, u32 bs) +{ + u32 val = readl(reg); + + val |= bs; + writel(val, reg); +} + +static void sdr_clr_bits(void __iomem *reg, u32 bs) +{ + u32 val = readl(reg); + + val &= ~bs; + writel(val, reg); +} + +static void phytium_mci_reset_hw(struct phytium_mci_host *host) +{ + sdr_set_bits(host->base + MCI_CNTRL, MCI_CNTRL_FIFO_RESET | MCI_CNTRL_DMA_RESET); + + while (readl(host->base + MCI_CNTRL) & (MCI_CNTRL_FIFO_RESET | MCI_CNTRL_DMA_RESET)) + cpu_relax(); + phytium_mci_send_cmd(host, MCI_CMD_UPD_CLK, 0); +} + +static void phytium_mci_update_external_clk(struct phytium_mci_host *host, u32 uhs_reg_value) +{ + writel(0, host->base + MCI_UHS_REG_EXT); + writel(uhs_reg_value, host->base + MCI_UHS_REG_EXT); + while (!(readl(host->base + MCI_CCLK_RDY) & 0x1)) + cpu_relax(); +} + +static void phytium_mci_prepare_data(struct phytium_mci_host *host, + struct mmc_request *mrq) +{ + struct mmc_data *data = mrq->data; + + if (!(data->host_cookie & MCI_PREPARE_FLAG)) { + data->host_cookie |= MCI_PREPARE_FLAG; + data->sg_count = dma_map_sg(host->dev, data->sg, data->sg_len, + mmc_get_dma_dir(data)); + } +} + +static void phytium_mci_unprepare_data(struct phytium_mci_host *host, + struct mmc_request *mrq) +{ + struct mmc_data *data = mrq->data; + + if (data->host_cookie & MCI_ASYNC_FLAG) + return; + + if (data->host_cookie & MCI_PREPARE_FLAG) { + dma_unmap_sg(host->dev, data->sg, data->sg_len, mmc_get_dma_dir(data)); + data->host_cookie &= ~MCI_PREPARE_FLAG; + } +} + +static void phytium_mci_send_cmd(struct phytium_mci_host *host, u32 cmd, u32 arg) +{ + int rc; + u32 data; + + writel(arg, host->base + MCI_CMDARG); + wmb(); /* drain writebuffer */ + + rc = readl_relaxed_poll_timeout(host->base + MCI_STATUS, + data, + !(data & MCI_STATUS_CARD_BUSY), + 0, 100 * 1000); + if (rc == -ETIMEDOUT) + pr_debug("%s %d, timeout mci_status: 0x%08x\n", __func__, __LINE__, data); + + writel(MCI_CMD_START | cmd, host->base + MCI_CMD); + + rc = readl_relaxed_poll_timeout(host->base + MCI_CMD, + data, + !(data & MCI_CMD_START), + 0, 100 * 1000); + if (rc == -ETIMEDOUT) + pr_debug("%s %d, timeout mci_cmd: 0x%08x\n", __func__, __LINE__, data); +} + +static void phytium_mci_update_cmd11(struct phytium_mci_host *host, u32 cmd) +{ + writel(MCI_CMD_START | cmd, host->base + MCI_CMD); + + while (readl(host->base + MCI_CMD) & MCI_CMD_START) + cpu_relax(); +} + +static void phytium_mci_set_clk(struct phytium_mci_host *host, struct mmc_ios *ios) +{ + u32 div = 0xff, drv = 0, sample = 0; + unsigned long clk_rate; + u32 mci_cmd_bits = MCI_CMD_UPD_CLK; + u32 cmd_reg; + u32 cur_cmd_index; + u32 first_uhs_div, tmp_ext_reg; + + cmd_reg = readl(host->base + MCI_CMD); + cur_cmd_index = cmd_reg & 0x3F; + + if (cur_cmd_index == SD_SWITCH_VOLTAGE) + mci_cmd_bits |= MCI_CMD_VOLT_SWITCH; + if (ios->clock) { + if (host->current_ios_clk == ios->clock) + return; + + dev_dbg(host->dev, "will change clock, host->clk_rate: %ld, ios->clock: %d\n", + host->clk_rate, ios->clock); + + if (ios->clock >= 25000000) + tmp_ext_reg = 0x202; + else if (ios->clock == 400000) + tmp_ext_reg = 0x502; + else + tmp_ext_reg = 0x302; + + phytium_mci_update_external_clk(host, tmp_ext_reg); + sdr_clr_bits(host->base + MCI_CLKENA, MCI_CLKENA_CCLK_ENABLE); + + if (cur_cmd_index == SD_SWITCH_VOLTAGE) + phytium_mci_update_cmd11(host, mci_cmd_bits | cmd_reg); + else + phytium_mci_send_cmd(host, mci_cmd_bits, 0); + + clk_rate = host->clk_rate; + first_uhs_div = 1 + ((tmp_ext_reg >> 8)&0xFF); + div = clk_rate / (2 * first_uhs_div * ios->clock); + if (div > 2) { + sample = div / 2 + 1; + drv = sample - 1; + writel((sample << 16) | (drv << 8) | (div & 0xff), + host->base + MCI_CLKDIV); + } else if (div == 2) { + drv = 0; + sample = 1; + writel((drv << 8) | (sample << 16) | (div & 0xff), + host->base + MCI_CLKDIV); + } + + dev_dbg(host->dev, "UHS_REG_EXT ext: %x, CLKDIV: %x\n", + readl(host->base + MCI_UHS_REG_EXT), readl(host->base + MCI_CLKDIV)); + + sdr_set_bits(host->base + MCI_CLKENA, MCI_CLKENA_CCLK_ENABLE); + + if (cur_cmd_index == SD_SWITCH_VOLTAGE) + phytium_mci_update_cmd11(host, mci_cmd_bits | cmd_reg); + else + phytium_mci_send_cmd(host, mci_cmd_bits, 0); + + host->current_ios_clk = ios->clock; + + dev_dbg(host->dev, "host->clk_rate: %ld, ios->clock: %d\n", + host->clk_rate, ios->clock); + } else { + host->current_ios_clk = 0; + sdr_clr_bits(host->base + MCI_CLKENA, MCI_CLKENA_CCLK_ENABLE); + + if (cur_cmd_index == SD_SWITCH_VOLTAGE) + phytium_mci_update_cmd11(host, mci_cmd_bits | cmd_reg); + else + phytium_mci_send_cmd(host, mci_cmd_bits, 0); + + sdr_clr_bits(host->base + MCI_UHS_REG_EXT, MCI_EXT_CLK_ENABLE); + dev_dbg(host->dev, "host->clk_rate: %ld, ios->clock: %d\n", + host->clk_rate, ios->clock); + } +} + +static inline u32 +phytium_mci_cmd_find_resp(struct phytium_mci_host *host, + struct mmc_request *mrq, + struct mmc_command *cmd) +{ + u32 resp; + + switch (mmc_resp_type(cmd)) { + case MMC_RSP_R1: + case MMC_RSP_R1B: + resp = 0x5; + break; + + case MMC_RSP_R2: + resp = 0x7; + break; + + case MMC_RSP_R3: + resp = 0x1; + break; + + case MMC_RSP_NONE: + default: + resp = 0x0; + break; + } + + return resp; +} + +static inline +u32 phytium_mci_cmd_prepare_raw_cmd(struct phytium_mci_host *host, + struct mmc_request *mrq, + struct mmc_command *cmd) +{ + u32 opcode = cmd->opcode; + u32 resp = phytium_mci_cmd_find_resp(host, mrq, cmd); + u32 rawcmd = ((opcode & 0x3f) | ((resp & 0x7) << 6)); + + if (opcode == MMC_GO_INACTIVE_STATE || + (opcode == SD_IO_RW_DIRECT && ((cmd->arg >> 9) & 0x1FFFF) == SDIO_CCCR_ABORT)) + rawcmd |= (0x1 << 14); + else if (opcode == SD_SWITCH_VOLTAGE) + rawcmd |= (0x1 << 28); + + if (test_and_clear_bit(MCI_CARD_NEED_INIT, &host->flags)) + rawcmd |= (0x1 << 15); + + if (cmd->data) { + struct mmc_data *data = cmd->data; + + rawcmd |= (0x1 << 9); + + if (data->flags & MMC_DATA_WRITE) + rawcmd |= (0x1 << 10); + } + + return (rawcmd | (0x1 << 29) | (0x1 << 31)); +} + +static inline void +phytium_mci_adma_write_desc(struct phytium_mci_host *host, + struct phytium_adma2_64_desc *desc, + dma_addr_t addr, u32 len, u32 attribute) +{ + desc->attribute = attribute; + desc->len = len; + desc->addr_lo = lower_32_bits(addr); + desc->addr_hi = upper_32_bits(addr); + dev_dbg(host->dev, "%s %d:addr_lo:0x%x ddr_hi:0x%x\n", __func__, + __LINE__, desc->addr_lo, desc->addr_hi); + + if ((attribute == 0x80000004) || (attribute == 0x8000000c)) { + desc->desc_lo = 0; + desc->desc_hi = 0; + } +} + +static void +phytium_mci_data_sg_write_2_admc_table(struct phytium_mci_host *host, struct mmc_data *data) +{ + struct phytium_adma2_64_desc *desc; + u32 dma_len, i; + dma_addr_t dma_address; + struct scatterlist *sg; + + phytium_mci_init_adma_table(host, &host->dma); + + desc = host->dma.adma_table; + for_each_sg(data->sg, sg, data->sg_count, i) { + dma_address = sg_dma_address(sg); + dma_len = sg_dma_len(sg); + + if (i == 0) { + if (sg_is_last(sg) || (data->sg_count == 1 && dma_len == SD_BLOCK_SIZE)) + phytium_mci_adma_write_desc(host, desc, dma_address, + dma_len, 0x8000000c); + else + phytium_mci_adma_write_desc(host, desc, dma_address, + dma_len, 0x8000001a); + } else if (sg_is_last(sg)) { + phytium_mci_adma_write_desc(host, desc, dma_address, + dma_len, 0x80000004); + } else { + phytium_mci_adma_write_desc(host, desc, dma_address, + dma_len, 0x80000012); + } + + desc++; + } +} + +static void +phytium_mci_data_sg_write_2_fifo(struct phytium_mci_host *host, struct mmc_data *data) +{ + struct scatterlist *sg; + u32 dma_len, i, j; + u32 *virt_addr; + + if (mmc_get_dma_dir(data) == DMA_TO_DEVICE) { + writel(0x1<<10, host->base + MCI_CMD); + for_each_sg(data->sg, sg, data->sg_count, i) { + dma_len = sg_dma_len(sg); + virt_addr = sg_virt(data->sg); + for (j = 0; j < (dma_len / 4); j++) { + writel(*virt_addr, host->base + MCI_DATA); + virt_addr++; + } + } + } +} + +static void phytium_mci_restart_clk(struct phytium_mci_host *host) +{ + u32 clk_div, uhs; + + while (readl(host->base + MCI_CMD) & MCI_CMD_START) + cpu_relax(); + sdr_clr_bits(host->base + MCI_CLKENA, MCI_CLKENA_CCLK_ENABLE); + clk_div = readl(host->base + MCI_CLKDIV); + uhs = readl(host->base + MCI_UHS_REG_EXT); + writel(0, host->base + MCI_UHS_REG_EXT); + writel(uhs, host->base + MCI_UHS_REG_EXT); + while (!(readl(host->base + MCI_CCLK_RDY) & 0x1)) + cpu_relax(); + + writel(clk_div, host->base + MCI_CLKDIV); + sdr_set_bits(host->base + MCI_CLKENA, MCI_CLKENA_CCLK_ENABLE); + writel(MCI_CMD_START | MCI_CMD_UPD_CLK, host->base + MCI_CMD); + while (readl(host->base + MCI_CMD) & MCI_CMD_START) + cpu_relax(); +} + +static int +phytim_mci_start_multiple_write(struct phytium_mci_host *host, + struct mmc_request *mrq, u32 cnts, u32 offset) +{ + u32 rawcmd, cmd_status; + struct mmc_command *cmd = mrq->cmd; + u32 *rsp = cmd->resp; + unsigned long deadline_time; + + if (!(host->mmc->caps & MMC_CAP_NONREMOVABLE) && readl(host->base + MCI_CARD_DETECT)) + return -ESHUTDOWN; + + while ((readl(host->base + MCI_STATUS) & (MCI_STATUS_CARD_BUSY))) + cpu_relax(); + + writel(0xffffe, host->base + MCI_RAW_INTS); + rawcmd = phytium_mci_cmd_prepare_raw_cmd(host, mrq, cmd); + writel(mrq->data->blksz, host->base + MCI_BLKSIZ); + writel(cnts * mrq->data->blksz, host->base + MCI_BYTCNT); + writel(cmd->arg + offset, host->base + MCI_CMDARG); + writel(rawcmd, host->base + MCI_CMD); + deadline_time = jiffies + msecs_to_jiffies(200); + + cmd_status = readl(host->base + MCI_RAW_INTS); + while (!(cmd_status & MCI_MASKED_INTS_CMD)) { + if (!(host->mmc->caps & MMC_CAP_NONREMOVABLE) && + readl(host->base + MCI_CARD_DETECT)) + return -ESHUTDOWN; + + cmd_status = readl(host->base + MCI_RAW_INTS); + if (cmd_err_ints_mask & cmd_status) + return -ESHUTDOWN; + + if (cmd_status & MCI_MASKED_INTS_CMD) + break; + + if (time_after(jiffies, deadline_time)) + return -ESHUTDOWN; + } + + if (cmd_status & MCI_MASKED_INTS_CMD) { + if (cmd->flags & MMC_RSP_136) { + rsp[3] = readl(host->base + MCI_RESP0); + rsp[2] = readl(host->base + MCI_RESP1); + rsp[1] = readl(host->base + MCI_RESP2); + rsp[0] = readl(host->base + MCI_RESP3); + } else { + rsp[0] = readl(host->base + MCI_RESP0); + } + } + deadline_time = jiffies + msecs_to_jiffies(1000); + while (!(cmd_status & MCI_MASKED_INTS_DTO)) { + if (!(host->mmc->caps & MMC_CAP_NONREMOVABLE) && + readl(host->base + MCI_CARD_DETECT)) + return -ESHUTDOWN; + cmd_status = readl(host->base + MCI_RAW_INTS); + if (cmd_err_ints_mask & cmd_status) + return -ESHUTDOWN; + if (cmd_status & MCI_MASKED_INTS_DTO) + return 0; + if (time_after(jiffies, deadline_time)) + return -ESHUTDOWN; + } + return 0; +} + +static int +phytium_mci_start_sbc_stop_cmd(struct phytium_mci_host *host, struct mmc_request *mrq, + struct mmc_command *cmd, u32 arg) +{ + u32 rawcmd, cmd_status; + u32 *rsp = cmd->resp; + unsigned long deadline_time; + + writel(0xffffe, host->base + MCI_RAW_INTS); + + while ((readl(host->base + MCI_STATUS) & (MCI_STATUS_CARD_BUSY))) + cpu_relax(); + + rawcmd = phytium_mci_cmd_prepare_raw_cmd(host, mrq, cmd); + writel(arg, host->base + MCI_CMDARG); + writel(rawcmd, host->base + MCI_CMD); + + deadline_time = jiffies + msecs_to_jiffies(200); + cmd_status = readl(host->base + MCI_RAW_INTS); + while (!(cmd_status & MCI_MASKED_INTS_CMD)) { + if (!(host->mmc->caps & MMC_CAP_NONREMOVABLE) && + readl(host->base + MCI_CARD_DETECT)) + return -ENOMEDIUM; + + cmd_status = readl(host->base + MCI_RAW_INTS); + if (cmd_err_ints_mask & cmd_status) + return -ETIMEDOUT; + + if (cmd_status & MCI_MASKED_INTS_CMD) + break; + + if (time_after(jiffies, deadline_time)) + return -ETIMEDOUT; + } + + if (cmd_status & MCI_MASKED_INTS_CMD) { + if (cmd->flags & MMC_RSP_136) { + rsp[3] = readl(host->base + MCI_RESP0); + rsp[2] = readl(host->base + MCI_RESP1); + rsp[1] = readl(host->base + MCI_RESP2); + rsp[0] = readl(host->base + MCI_RESP3); + } else { + rsp[0] = readl(host->base + MCI_RESP0); + } + } + + if (cmd_err_ints_mask & cmd_status) + return -ETIMEDOUT; + + return 0; +} + +static void +phytium_mci_start_write_multiple_non_dma(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct phytium_mci_host *host = mmc_priv(mmc); + struct mmc_data *data = mrq->data; + u32 write_cnts, last_cnts; + u32 i, j, k, send_cnt_one_sg, block_offset; + int ret = 0, dma_len; + struct scatterlist *sg; + u32 *virt_addr = NULL; + + write_cnts = data->blocks / 4; + (data->blocks % 4) ? write_cnts++ : write_cnts; + last_cnts = data->blocks % 4; + if (!(host->mmc->caps & MMC_CAP_NONREMOVABLE) && readl(host->base + MCI_CARD_DETECT)) { + ret = -ENOMEDIUM; + goto write_err; + } + + dev_dbg(host->dev, "%s: cmd:%d, block counts:%d\n", + __func__, mrq->cmd->opcode, data->blocks); + + sdr_clr_bits(host->base + MCI_CNTRL, MCI_CNTRL_USE_INTERNAL_DMAC); + sdr_set_bits(host->base + MCI_CNTRL, MCI_CNTRL_FIFO_RESET); + while (readl(host->base + MCI_CNTRL) & MCI_CNTRL_FIFO_RESET) + cpu_relax(); + sdr_clr_bits(host->base + MCI_BUS_MODE, MCI_BUS_MODE_DE); + + if (mmc_get_dma_dir(data) == DMA_TO_DEVICE) { + block_offset = 0; + for_each_sg(data->sg, sg, data->sg_count, i) { + /* Each SG data transfor starts */ + dma_len = sg_dma_len(sg); + send_cnt_one_sg = (dma_len / MCI_MAX_FIFO_CNT) + 1; + virt_addr = sg_virt(sg); + for (k = 0; k < send_cnt_one_sg; k++) { + if (dma_len && dma_len >= MCI_MAX_FIFO_CNT) { + /*first write sbc cmd*/ + ret = phytium_mci_start_sbc_stop_cmd(host, mrq, + mrq->sbc, 4); + if (ret) + goto write_err; + writel(0x1 << 10, host->base + MCI_CMD); + for (j = 0; j < (MCI_MAX_FIFO_CNT / 4); j++) { + writel(*virt_addr, host->base + MCI_DATA); + virt_addr++; + } + + /*second write cmd25 here*/ + ret = phytim_mci_start_multiple_write(host, mrq, 4, + block_offset); + if (ret) + goto write_err; + block_offset += 4; + dma_len -= MCI_MAX_FIFO_CNT; + } else if (dma_len > 0) { + /*first write sbc cmd*/ + last_cnts = dma_len / 512; + ret = phytium_mci_start_sbc_stop_cmd(host, mrq, mrq->sbc, + last_cnts); + if (ret) + goto write_err; + writel(0x1 << 10, host->base + MCI_CMD); + for (j = 0; j < (dma_len / 4); j++) { + writel(*virt_addr, host->base + MCI_DATA); + virt_addr++; + } + /*second write cmd25 here*/ + ret = phytim_mci_start_multiple_write(host, mrq, last_cnts, + block_offset); + if (ret) + goto write_err; + block_offset += last_cnts; + dma_len = 0; + } else { + dev_dbg(host->dev, "%s: sg %d end\n", __func__, i); + break; + } + } + } + } + +write_err: + host->data = NULL; + host->cmd = NULL; + host->mrq = NULL; + writel(0xffffe, host->base + MCI_RAW_INTS); + if (ret) { + data->bytes_xfered = 0; + if (ret == -ESHUTDOWN) { + sdr_set_bits(host->base + MCI_CNTRL, MCI_CNTRL_FIFO_RESET); + while (readl(host->base + MCI_CNTRL) & MCI_CNTRL_FIFO_RESET) + cpu_relax(); + + sdr_set_bits(host->base + MCI_CNTRL, MCI_CNTRL_CONTROLLER_RESET); + while (readl(host->base + MCI_STATUS) & MCI_STATUS_CARD_BUSY) + sdr_set_bits(host->base + MCI_CNTRL, MCI_CNTRL_CONTROLLER_RESET); + phytium_mci_restart_clk(host); + phytium_mci_start_sbc_stop_cmd(host, mrq, mrq->stop, mrq->stop->arg); + } + data->error = -ETIMEDOUT; + mrq->cmd->error = -ETIMEDOUT; + mmc_request_done(host->mmc, mrq); + return; + } + data->bytes_xfered = data->blocks * data->blksz; + mmc_request_done(host->mmc, mrq); +} + +static void +phytium_mci_start_data(struct phytium_mci_host *host, struct mmc_request *mrq, + struct mmc_command *cmd, struct mmc_data *data) +{ + bool read; + u32 rawcmd; + unsigned long flags; + + + WARN_ON(host->cmd); + host->cmd = cmd; + cmd->error = 0; + WARN_ON(host->data); + host->data = data; + read = data->flags & MMC_DATA_READ; + + if (!(host->mmc->caps & MMC_CAP_NONREMOVABLE) && readl(host->base + MCI_CARD_DETECT)) { + phytium_mci_err_irq(host, 0, MCI_INT_MASK_RTO); + return; + } + /* clear interrupts */ + writel(0xffffe, host->base + MCI_RAW_INTS); + + sdr_set_bits(host->base + MCI_CNTRL, MCI_CNTRL_FIFO_RESET | MCI_CNTRL_DMA_RESET); + + while (readl(host->base + MCI_CNTRL) & (MCI_CNTRL_FIFO_RESET | MCI_CNTRL_DMA_RESET)) + cpu_relax(); + + if (host->adtc_type == COMMOM_ADTC) + sdr_clr_bits(host->base + MCI_CNTRL, MCI_CNTRL_USE_INTERNAL_DMAC); + else + sdr_set_bits(host->base + MCI_CNTRL, MCI_CNTRL_USE_INTERNAL_DMAC); + wmb(); /* drain writebuffer */ + sdr_clr_bits(host->base + MCI_CNTRL, MCI_CNTRL_INT_ENABLE); + + rawcmd = phytium_mci_cmd_prepare_raw_cmd(host, mrq, cmd); + if (host->is_use_dma && host->adtc_type == BLOCK_RW_ADTC) + phytium_mci_data_sg_write_2_admc_table(host, data); + else + phytium_mci_data_sg_write_2_fifo(host, data); + + spin_lock_irqsave(&host->lock, flags); + sdr_set_bits(host->base + MCI_INT_MASK, cmd_ints_mask | data_ints_mask); + if (host->is_use_dma && host->adtc_type == BLOCK_RW_ADTC) { + sdr_set_bits(host->base + MCI_DMAC_INT_ENA, dmac_ints_mask); + /* Enable the IDMAC */ + sdr_set_bits(host->base + MCI_BUS_MODE, MCI_BUS_MODE_DE); + writel((u32)host->dma.adma_addr, host->base + MCI_DESC_LIST_ADDRL); + writel((u32)(host->dma.adma_addr >> 32), host->base + MCI_DESC_LIST_ADDRH); + } + writel(mrq->data->blksz, host->base + MCI_BLKSIZ); + writel(mrq->data->blocks * mrq->data->blksz, host->base + MCI_BYTCNT); + sdr_set_bits(host->base + MCI_CNTRL, MCI_CNTRL_INT_ENABLE); + writel(cmd->arg, host->base + MCI_CMDARG); + wmb(); /* drain writebuffer */ + writel(rawcmd, host->base + MCI_CMD); + spin_unlock_irqrestore(&host->lock, flags); + + mod_timer(&host->timeout_timer, + jiffies + msecs_to_jiffies(MMC_REQ_TIMEOUT_MS)); +} + +static void phytium_mci_track_cmd_data(struct phytium_mci_host *host, + struct mmc_command *cmd, + struct mmc_data *data) +{ + if (host->error) + dev_dbg(host->dev, "%s: cmd=%d arg=%08X; host->error=0x%08X\n", + __func__, cmd->opcode, cmd->arg, host->error); +} + +static void phytium_mci_request_done(struct phytium_mci_host *host, struct mmc_request *mrq) +{ + unsigned long flags; + + spin_lock_irqsave(&host->lock, flags); + del_timer(&host->timeout_timer); + host->mrq = NULL; + if (host->cmd) + host->cmd = NULL; + spin_unlock_irqrestore(&host->lock, flags); + phytium_mci_track_cmd_data(host, mrq->cmd, mrq->data); + + if (mrq->data) + phytium_mci_unprepare_data(host, mrq); + + mmc_request_done(host->mmc, mrq); +} + +static bool phytium_mci_cmd_done(struct phytium_mci_host *host, int events, + struct mmc_request *mrq, struct mmc_command *cmd) +{ + bool done = false; + unsigned long flags; + u32 *rsp = cmd->resp; + + if (!(events & (MCI_RAW_INTS_RCRC | MCI_RAW_INTS_RE | MCI_RAW_INTS_CMD | + MCI_RAW_INTS_RTO | MCI_INT_MASK_HTO))) { + dev_err(host->dev, "No interrupt generation:h%x\n", events); + return done; + } + + spin_lock_irqsave(&host->lock, flags); + done = !host->cmd; + host->cmd = NULL; + if (done) { + spin_unlock_irqrestore(&host->lock, flags); + return true; + } + sdr_clr_bits(host->base + MCI_INT_MASK, cmd_ints_mask); + spin_unlock_irqrestore(&host->lock, flags); + + if (cmd->flags & MMC_RSP_PRESENT) { + if (cmd->flags & MMC_RSP_136) { + rsp[3] = readl(host->base + MCI_RESP0); + rsp[2] = readl(host->base + MCI_RESP1); + rsp[1] = readl(host->base + MCI_RESP2); + rsp[0] = readl(host->base + MCI_RESP3); + } else { + /* + * Sometimes get ACMD41 cmd done irq but the respose index still the APP_CMD, + * so polling the mci status entill the respose index change. + */ + if (cmd->opcode == SD_APP_OP_COND) { + int polling_cnt = 20; + while (MMC_APP_CMD == MCI_STATUS_RESPOSE_INDEX(readl(host->base + MCI_STATUS))) { + udelay(100); + polling_cnt --; + if (polling_cnt == 0) { + dev_info(host->dev, "hw respose index not equal cmd opcode, respose value may error\n"); + break; + } + } + } + rsp[0] = readl(host->base + MCI_RESP0); + } + + if (cmd->opcode == SD_SEND_RELATIVE_ADDR) + host->current_rca = rsp[0] & 0xFFFF0000; + } + if (!(events & (MCI_RAW_INTS_CMD | MCI_INT_MASK_HTO))) { + if (!(host->mmc->caps & MMC_CAP_NONREMOVABLE) && (events & MCI_RAW_INTS_RTO) + && readl(host->base + MCI_CARD_DETECT)) { + cmd->error = -ENOMEDIUM; + rsp[0] = 0; + } else if (events & MCI_RAW_INTS_RTO || + (cmd->opcode != MMC_SEND_TUNING_BLOCK && + cmd->opcode != MMC_SEND_TUNING_BLOCK_HS200)) { + cmd->error = -ETIMEDOUT; + } else if (events & MCI_RAW_INTS_RCRC) { + cmd->error = -EILSEQ; + } else { + cmd->error = -ETIMEDOUT; + } + } + phytium_mci_cmd_next(host, mrq, cmd); + return true; +} + +static void phytium_mci_start_command(struct phytium_mci_host *host, + struct mmc_request *mrq, + struct mmc_command *cmd) +{ + u32 rawcmd; + unsigned long flags; + + spin_lock_irqsave(&host->lock, flags); + WARN_ON(host->cmd); + host->cmd = cmd; + cmd->error = 0; + writel(0xffffe, host->base + MCI_RAW_INTS); + + rawcmd = phytium_mci_cmd_prepare_raw_cmd(host, mrq, cmd); + spin_unlock_irqrestore(&host->lock, flags); + + if (!(host->mmc->caps & MMC_CAP_NONREMOVABLE) && readl(host->base + MCI_CARD_DETECT)) { + phytium_mci_cmd_done(host, MCI_RAW_INTS_RTO, mrq, cmd); + return; + } + + spin_lock_irqsave(&host->lock, flags); + sdr_set_bits(host->base + MCI_INT_MASK, cmd_ints_mask); + writel(cmd->arg, host->base + MCI_CMDARG); + writel(rawcmd, host->base + MCI_CMD); + spin_unlock_irqrestore(&host->lock, flags); + + mod_timer(&host->timeout_timer, + jiffies + msecs_to_jiffies(MMC_REQ_TIMEOUT_MS)); +} + +static void +phytium_mci_cmd_next(struct phytium_mci_host *host, struct mmc_request *mrq, + struct mmc_command *cmd) +{ + if ((cmd->error && !(cmd->opcode == MMC_SEND_TUNING_BLOCK || + cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200)) || + (mrq->sbc && mrq->sbc->error)) { + phytium_mci_request_done(host, mrq); + } else if (cmd == mrq->sbc) { + if ((mrq->cmd->opcode == MMC_READ_MULTIPLE_BLOCK) || + (mrq->cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK) || + (mrq->cmd->opcode == MMC_READ_SINGLE_BLOCK) || + (mrq->cmd->opcode == MMC_WRITE_BLOCK)) { + dev_dbg(host->dev, "%s %d:sbc done and next cmd :%d length:%d\n", + __func__, __LINE__, mrq->cmd->opcode, mrq->data->sg->length); + phytium_mci_prepare_data(host, mrq); + if (host->is_use_dma) + host->adtc_type = BLOCK_RW_ADTC; + else + host->adtc_type = COMMOM_ADTC; + phytium_mci_start_data(host, mrq, mrq->cmd, mrq->data); + } else { + dev_err(host->dev, "%s %d:ERROR: cmd %d followers the SBC\n", + __func__, __LINE__, cmd->opcode); + } + } else if (!cmd->data) { + phytium_mci_request_done(host, mrq); + } +} + +static void phytium_mci_ops_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct phytium_mci_host *host = mmc_priv(mmc); + u32 data; + int rc; + + host->error = 0; + WARN_ON(host->mrq); + host->mrq = mrq; + + rc = readl_relaxed_poll_timeout(host->base + MCI_STATUS, + data, + !(data & MCI_STATUS_CARD_BUSY), + 0, 500 * 1000); + if (rc == -ETIMEDOUT) + pr_debug("%s %d, timeout mci_status: 0x%08x\n", __func__, __LINE__, data); + + dev_dbg(host->dev, "%s %d: cmd:%d arg:0x%x\n", __func__, __LINE__, + mrq->cmd->opcode, mrq->cmd->arg); + + if (host->is_device_x100 && mrq->sbc && mrq->cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK) { + phytium_mci_start_write_multiple_non_dma(mmc, mrq); + return; + } + + if (mrq->sbc) { + phytium_mci_start_command(host, mrq, mrq->sbc); + return; + } + if (mrq->data) { + phytium_mci_prepare_data(host, mrq); + + if ((mrq->data->sg->length >= 512) && host->is_use_dma && + ((mrq->cmd->opcode == MMC_READ_MULTIPLE_BLOCK) || + (mrq->cmd->opcode == MMC_READ_SINGLE_BLOCK) || + (mrq->cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK) || + (mrq->cmd->opcode == MMC_WRITE_BLOCK) || + (mrq->cmd->opcode == SD_IO_RW_EXTENDED))) + + host->adtc_type = BLOCK_RW_ADTC; + else + host->adtc_type = COMMOM_ADTC; + + phytium_mci_start_data(host, mrq, mrq->cmd, mrq->data); + return; + } + phytium_mci_start_command(host, mrq, mrq->cmd); +} + +static void phytium_mci_pre_req(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct phytium_mci_host *host = mmc_priv(mmc); + struct mmc_data *data = mrq->data; + + if (!data) + return; + + phytium_mci_prepare_data(host, mrq); + data->host_cookie |= MCI_ASYNC_FLAG; +} + +static void phytium_mci_post_req(struct mmc_host *mmc, struct mmc_request *mrq, + int err) +{ + struct phytium_mci_host *host = mmc_priv(mmc); + struct mmc_data *data = mrq->data; + + if (!data) + return; + + if (data->host_cookie & MCI_ASYNC_FLAG) { + data->host_cookie &= ~MCI_ASYNC_FLAG; + phytium_mci_unprepare_data(host, mrq); + } +} + +static void phytium_mci_data_read_without_dma(struct phytium_mci_host *host, + struct mmc_data *data) +{ + u32 length, i, data_val, dma_len, tmp = 0; + u32 *virt_addr; + unsigned long flags; + struct scatterlist *sg; + + length = data->blocks * data->blksz; + + if (mmc_get_dma_dir(data) == DMA_FROM_DEVICE) { + spin_lock_irqsave(&host->lock, flags); + if (data->host_cookie & MCI_ASYNC_FLAG) { + tmp = MCI_ASYNC_FLAG; + phytium_mci_post_req(host->mmc, data->mrq, 0); + } else { + phytium_mci_unprepare_data(host, data->mrq); + } + + for_each_sg(data->sg, sg, data->sg_count, i) { + dma_len = sg_dma_len(sg); + virt_addr = sg_virt(data->sg); + + for (i = 0; i < (dma_len / 4); i++) { + data_val = readl(host->base + MCI_DATA); + memcpy(virt_addr, &data_val, 4); + ++virt_addr; + } + } + + if (tmp & MCI_ASYNC_FLAG) + phytium_mci_pre_req(host->mmc, data->mrq); + else + phytium_mci_prepare_data(host, data->mrq); + + spin_unlock_irqrestore(&host->lock, flags); + } + data->bytes_xfered = length; +} + +static void phytium_mci_data_xfer_next(struct phytium_mci_host *host, + struct mmc_request *mrq, + struct mmc_data *data) +{ + if (mmc_op_multi(mrq->cmd->opcode) && mrq->stop && + (data->error || !mrq->sbc)) { + while ((readl(host->base + MCI_STATUS) & (MCI_STATUS_CARD_BUSY))) + cpu_relax(); + phytium_mci_start_command(host, mrq, mrq->stop); + } else { + phytium_mci_request_done(host, mrq); + } +} + +static bool phytium_mci_data_xfer_done(struct phytium_mci_host *host, u32 events, + struct mmc_request *mrq, struct mmc_data *data) +{ + unsigned long flags; + bool done; + + unsigned int check_data = events & (MCI_RAW_INTS_DTO | MCI_RAW_INTS_RCRC | + MCI_RAW_INTS_DCRC | MCI_RAW_INTS_RE | + MCI_RAW_INTS_DRTO | MCI_RAW_INTS_EBE | + MCI_DMAC_STATUS_AIS | MCI_DMAC_STATUS_DU | + MCI_RAW_INTS_SBE_BCI | MCI_INT_MASK_RTO); + + spin_lock_irqsave(&host->lock, flags); + done = !host->data; + + if (check_data || host->data) + host->data = NULL; + spin_unlock_irqrestore(&host->lock, flags); + + if (done) + return true; + if (check_data) { + spin_lock_irqsave(&host->lock, flags); + sdr_clr_bits(host->base + MCI_DMAC_INT_ENA, dmac_ints_mask); + sdr_clr_bits(host->base + MCI_INT_MASK, data_ints_mask); + /* Stop the IDMAC running */ + sdr_clr_bits(host->base + MCI_BUS_MODE, MCI_BUS_MODE_DE); + dev_dbg(host->dev, "DMA stop\n"); + spin_unlock_irqrestore(&host->lock, flags); + + if (events & MCI_RAW_INTS_DTO) { + if (!host->is_use_dma || + (host->is_use_dma && host->adtc_type == COMMOM_ADTC && + (mrq->cmd->flags & MMC_CMD_MASK) == MMC_CMD_ADTC)) + phytium_mci_data_read_without_dma(host, data); + else + data->bytes_xfered = data->blocks * data->blksz; + } else { + data->bytes_xfered = 0; + if (!(host->mmc->caps & MMC_CAP_NONREMOVABLE) + && readl(host->base + MCI_CARD_DETECT) + && (events & cmd_err_ints_mask)) { + data->error = -ENOMEDIUM; + data->mrq->cmd->error = -ENOMEDIUM; + } else if (events & (MCI_RAW_INTS_DCRC | MCI_RAW_INTS_EBE | + MCI_RAW_INTS_SBE_BCI)) { + data->error = -EILSEQ; + host->cmd = NULL; + } else { + data->error = -ETIMEDOUT; + host->cmd = NULL; + } + } + + phytium_mci_data_xfer_next(host, mrq, data); + done = true; + } + return done; +} + +static int phytium_mci_card_busy(struct mmc_host *mmc) +{ + struct phytium_mci_host *host = mmc_priv(mmc); + u32 status; + + status = readl(host->base + MCI_STATUS); + + return !!(status & MCI_STATUS_CARD_BUSY); +} + +static void __phytium_mci_enable_sdio_irq(struct phytium_mci_host *host, int enable) +{ + if (enable) + sdr_set_bits(host->base + MCI_INT_MASK, MCI_INT_MASK_SDIO); + else + sdr_clr_bits(host->base + MCI_INT_MASK, MCI_INT_MASK_SDIO); +} + +static void phytium_mci_enable_sdio_irq(struct mmc_host *mmc, int enable) +{ + struct phytium_mci_host *host = mmc_priv(mmc); + + __phytium_mci_enable_sdio_irq(host, enable); +} + +static void hotplug_timer_func(struct timer_list *t) +{ + struct phytium_mci_host *host; + u32 status; + + host = from_timer(host, t, hotplug_timer); + if (!host) + return; + + status = readl(host->base + MCI_CARD_DETECT); + + if (status & 0x1) { + if (host->mmc->card) { + cancel_delayed_work(&host->mmc->detect); + mmc_detect_change(host->mmc, msecs_to_jiffies(100)); + } + } else { + cancel_delayed_work(&host->mmc->detect); + mmc_detect_change(host->mmc, msecs_to_jiffies(200)); + } +} + +static void phytium_mci_request_timeout(struct timer_list *t) +{ + struct phytium_mci_host *host = from_timer(host, t, timeout_timer); + + dev_err(host->dev, "request timeout\n"); + if (host->mrq) { + dev_err(host->dev, "aborting mrq=%p cmd=%d\n", + host->mrq, host->mrq->cmd->opcode); + if (host->cmd) { + dev_err(host->dev, "aborting cmd=%d\n", host->cmd->opcode); + phytium_mci_cmd_done(host, MCI_RAW_INTS_RTO, host->mrq, host->cmd); + } else if (host->data) { + dev_err(host->dev, "abort data: cmd%d; %d blocks\n", + host->mrq->cmd->opcode, host->data->blocks); + phytium_mci_data_xfer_done(host, MCI_RAW_INTS_RTO, host->mrq, + host->data); + } + } +} + +static int phytium_mci_err_irq(struct phytium_mci_host *host, u32 dmac_events, u32 events) +{ + struct mmc_request *mrq; + struct mmc_command *cmd; + struct mmc_data *data; + + mrq = host->mrq; + cmd = host->cmd; + data = host->data; + + if (cmd && (cmd == mrq->sbc)) { + phytium_mci_cmd_done(host, MCI_RAW_INTS_RTO, mrq, mrq->sbc); + } else if (cmd && (cmd == mrq->stop)) { + phytium_mci_cmd_done(host, MCI_RAW_INTS_RTO, mrq, mrq->stop); + } else if (data) { + data->error = -ETIMEDOUT; + if ((data->flags & MMC_DATA_READ) == MMC_DATA_READ || + (data->flags & MMC_DATA_WRITE) == MMC_DATA_WRITE) + phytium_mci_data_xfer_done(host, events | dmac_events, mrq, data); + } else if (cmd) { + phytium_mci_cmd_done(host, MCI_RAW_INTS_RTO, mrq, mrq->cmd); + } + + return 0; +} + +static irqreturn_t phytium_mci_irq(int irq, void *dev_id) +{ + struct phytium_mci_host *host = (struct phytium_mci_host *) dev_id; + unsigned long flags; + struct mmc_request *mrq; + struct mmc_command *cmd; + struct mmc_data *data; + u32 events, event_mask, dmac_events, dmac_evt_mask; + + if (!host) + return IRQ_NONE; + writel(0, host->base + 0xfd0); + + spin_lock_irqsave(&host->lock, flags); + events = readl(host->base + MCI_RAW_INTS); + dmac_events = readl(host->base + MCI_DMAC_STATUS); + event_mask = readl(host->base + MCI_INT_MASK); + dmac_evt_mask = readl(host->base + MCI_DMAC_INT_ENA); + if ((!events) && (!(dmac_events&0x1fff))) { + spin_unlock_irqrestore(&host->lock, flags); + return IRQ_NONE; + } + dev_dbg(host->dev, "%s:events:%x,mask:0x%x,dmac_events:%x,dmac_mask:0x%x,cmd:%d\n", + __func__, events, event_mask, dmac_events, dmac_evt_mask, + host->mrq ? host->mrq->cmd->opcode : 255); + + mrq = host->mrq; + cmd = host->cmd; + data = host->data; + +#if 0 + if (((events & event_mask) & MCI_RAW_INTS_SDIO) && + ((events == 0x10001) || (events == 0x10000) || (events == 0x10040))) { + writel(events, host->base + MCI_RAW_INTS); + __phytium_mci_enable_sdio_irq(host, 0); + sdio_signal_irq(host->mmc); + spin_unlock_irqrestore(&host->lock, flags); + goto irq_out; + } +#endif + if ((events & event_mask) & MCI_RAW_INTS_SDIO) { + __phytium_mci_enable_sdio_irq(host, 0); + } + + + writel(events, host->base + MCI_RAW_INTS); + writel(dmac_events, host->base + MCI_DMAC_STATUS); + spin_unlock_irqrestore(&host->lock, flags); + + if ((events & event_mask) & MCI_RAW_INTS_SDIO) { + sdio_signal_irq(host->mmc); + } + + if (((events & event_mask) == 0) && ((dmac_evt_mask & dmac_events) == 0)) + goto irq_out; + + if (((events & event_mask) & MCI_RAW_INTS_CD) && + !(host->mmc->caps & MMC_CAP_NONREMOVABLE)) { + mod_timer(&host->hotplug_timer, jiffies + usecs_to_jiffies(20000)); + dev_dbg(host->dev, "sd status changed here ! status:[%d] [%s %d]", + readl(host->base + MCI_CARD_DETECT), __func__, __LINE__); + + if ((events & event_mask) == MCI_RAW_INTS_CD) + goto irq_out; + } + + if (!mrq) { + if (events & MCI_RAW_INTS_HLE) + dev_dbg(host->dev, + "%s: MRQ=NULL and HW write locked, events=%08x,event_mask=%08x\n", + __func__, events, event_mask); + else + dev_dbg(host->dev, "%s: MRQ=NULL events:%08X evt_mask=%08X,sd_status:%d\n", + __func__, events, event_mask, readl(host->base + MCI_CARD_DETECT)); + goto irq_out; + } + + if ((dmac_events & dmac_err_ints_mask) || (events & cmd_err_ints_mask)) { + dev_dbg(host->dev, "ERR:events:%x,mask:0x%x,dmac_evts:%x,dmac_mask:0x%x,cmd:%d\n", + events, event_mask, dmac_events, dmac_evt_mask, mrq->cmd->opcode); + phytium_mci_err_irq(host, dmac_events & dmac_err_ints_mask, + events & cmd_err_ints_mask); + goto irq_out; + } + + if ((events & MCI_MASKED_INTS_DTO) && (events & MCI_MASKED_INTS_CMD)) { + phytium_mci_cmd_done(host, events, mrq, cmd); + phytium_mci_data_xfer_done(host, (events & data_ints_mask) | + (dmac_events & dmac_ints_mask), mrq, data); + } else if (events & MCI_MASKED_INTS_CMD || + ((events & MCI_INT_MASK_HTO) && (cmd->opcode == SD_SWITCH_VOLTAGE))) { + phytium_mci_cmd_done(host, events, mrq, cmd); + } else if (events & MCI_MASKED_INTS_DTO) { + phytium_mci_data_xfer_done(host, (events & data_ints_mask) | + (dmac_events & dmac_ints_mask), mrq, data); + } + +irq_out: + return IRQ_HANDLED; +} + +static void phytium_mci_init_hw(struct phytium_mci_host *host) +{ + u32 val; + int uhs_reg_value = 0x502; + + writel(MCI_SET_FIFOTH(0x2, 0x7, 0x100), host->base + MCI_FIFOTH); + writel(0x800001, host->base + MCI_CARD_THRCTL); + sdr_clr_bits(host->base + MCI_CLKENA, MCI_CLKENA_CCLK_ENABLE); + phytium_mci_update_external_clk(host, uhs_reg_value); + + sdr_set_bits(host->base + MCI_PWREN, MCI_PWREN_ENABLE); + sdr_set_bits(host->base + MCI_CLKENA, MCI_CLKENA_CCLK_ENABLE); + sdr_set_bits(host->base + MCI_UHS_REG_EXT, MCI_EXT_CLK_ENABLE); + sdr_clr_bits(host->base + MCI_UHS_REG, MCI_UHS_REG_VOLT); + + phytium_mci_reset_hw(host); + + if (host->mmc->caps & MMC_CAP_NONREMOVABLE) + sdr_set_bits(host->base + MCI_CARD_RESET, MCI_CARD_RESET_ENABLE); + else + sdr_clr_bits(host->base + MCI_CARD_RESET, MCI_CARD_RESET_ENABLE); + + writel(0, host->base + MCI_INT_MASK); + val = readl(host->base + MCI_RAW_INTS); + writel(val, host->base + MCI_RAW_INTS); + writel(0, host->base + MCI_DMAC_INT_ENA); + val = readl(host->base + MCI_DMAC_STATUS); + writel(val, host->base + MCI_DMAC_STATUS); + if (!(host->mmc->caps & MMC_CAP_NONREMOVABLE)) + writel(MCI_INT_MASK_CD, host->base + MCI_INT_MASK); + + sdr_set_bits(host->base + MCI_CNTRL, MCI_CNTRL_INT_ENABLE | + MCI_CNTRL_USE_INTERNAL_DMAC); + + writel(0xFFFFFFFF, host->base + MCI_TMOUT); + dev_info(host->dev, "init hardware done!"); + +} + +void phytium_mci_deinit_hw(struct phytium_mci_host *host) +{ + u32 val; + + sdr_clr_bits(host->base + MCI_PWREN, MCI_PWREN_ENABLE); + sdr_clr_bits(host->base + MCI_CLKENA, MCI_CLKENA_CCLK_ENABLE); + sdr_clr_bits(host->base + MCI_UHS_REG_EXT, MCI_EXT_CLK_ENABLE); + sdr_clr_bits(host->base + MCI_UHS_REG, MCI_UHS_REG_VOLT); + writel(0, host->base + MCI_INT_MASK); + val = readl(host->base + MCI_RAW_INTS); + writel(val, host->base + MCI_RAW_INTS); + writel(0, host->base + MCI_DMAC_INT_ENA); + val = readl(host->base + MCI_DMAC_STATUS); + writel(val, host->base + MCI_DMAC_STATUS); + if (!(host->mmc->caps & MMC_CAP_NONREMOVABLE)) + writel(MCI_INT_MASK_CD, host->base + MCI_INT_MASK); +} +EXPORT_SYMBOL_GPL(phytium_mci_deinit_hw); + +static void phytium_mci_adma_reset(struct phytium_mci_host *host) +{ + u32 bmod = readl(host->base + MCI_BUS_MODE); + + bmod |= MCI_BUS_MODE_SWR; + writel(bmod, host->base + MCI_BUS_MODE); +} + +static void phytium_mci_init_adma_table(struct phytium_mci_host *host, + struct phytium_mci_dma *dma) +{ + struct phytium_adma2_64_desc *adma_table = dma->adma_table; + dma_addr_t dma_addr; + int i; + + memset(adma_table, 0, sizeof(struct phytium_adma2_64_desc) * MAX_BD_NUM); + + for (i = 0; i < (MAX_BD_NUM - 1); i++) { + dma_addr = dma->adma_addr + sizeof(*adma_table) * (i + 1); + adma_table[i].desc_lo = lower_32_bits(dma_addr); + adma_table[i].desc_hi = upper_32_bits(dma_addr); + adma_table[i].attribute = 0; + adma_table[i].NON1 = 0; + adma_table[i].len = 0; + adma_table[i].NON2 = 0; + } + + phytium_mci_adma_reset(host); +} + +static void phytium_mci_set_buswidth(struct phytium_mci_host *host, u32 width) +{ + u32 val; + + switch (width) { + case MMC_BUS_WIDTH_1: + val = MCI_BUS_1BITS; + break; + + case MMC_BUS_WIDTH_4: + val = MCI_BUS_4BITS; + break; + + case MMC_BUS_WIDTH_8: + val = MCI_BUS_8BITS; + break; + default: + val = MCI_BUS_4BITS; + break; + } + writel(val, host->base + MCI_CTYPE); + dev_dbg(host->dev, "Bus Width = %d, set value:0x%x\n", width, val); +} + +static void phytium_mci_ops_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct phytium_mci_host *host = mmc_priv(mmc); + + if (ios->timing == MMC_TIMING_MMC_DDR52 || ios->timing == MMC_TIMING_UHS_DDR50) + sdr_set_bits(host->base + MCI_UHS_REG, MCI_UHS_REG_DDR); + else + sdr_clr_bits(host->base + MCI_UHS_REG, MCI_UHS_REG_DDR); + + phytium_mci_set_buswidth(host, ios->bus_width); + + switch (ios->power_mode) { + case MMC_POWER_UP: + set_bit(MCI_CARD_NEED_INIT, &host->flags); + writel(MCI_POWER_ON, host->base + MCI_PWREN); + break; + + case MMC_POWER_ON: + break; + + case MMC_POWER_OFF: + writel(MCI_POWER_OFF, host->base + MCI_PWREN); + break; + + default: + break; + } + phytium_mci_set_clk(host, ios); +} + +static void phytium_mci_ack_sdio_irq(struct mmc_host *mmc) +{ + unsigned long flags; + struct phytium_mci_host *host = mmc_priv(mmc); + + spin_lock_irqsave(&host->lock, flags); + __phytium_mci_enable_sdio_irq(host, 1); + spin_unlock_irqrestore(&host->lock, flags); +} + +static int phytium_mci_get_cd(struct mmc_host *mmc) +{ + struct phytium_mci_host *host = mmc_priv(mmc); + u32 status; + + if (mmc->caps & MMC_CAP_NONREMOVABLE) + return 1; + + status = readl(host->base + MCI_CARD_DETECT); + + if ((status & 0x1) == 0x1) + return 0; + + return 1; +} + +static int phytium_mci_ops_switch_volt(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct phytium_mci_host *host = mmc_priv(mmc); + unsigned int is_voltage_180 = 0; + + is_voltage_180 = readl(host->base + MCI_UHS_REG); + if ((mmc->caps & MMC_CAP_NONREMOVABLE) && (ios->signal_voltage != MMC_SIGNAL_VOLTAGE_180)) + return -EINVAL; + + if ((ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) && (is_voltage_180 & 0x1)) + sdr_clr_bits(host->base + MCI_UHS_REG, MCI_UHS_REG_VOLT); + else if ((ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180) && (!(is_voltage_180 & 0x1))) + sdr_set_bits(host->base + MCI_UHS_REG, MCI_UHS_REG_VOLT); + else if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_120) + return -EINVAL; + return 0; +} + +static void phytium_mci_hw_reset(struct mmc_host *mmc) +{ + struct phytium_mci_host *host = mmc_priv(mmc); + u32 reset_flag; + + if (host->is_use_dma) { + reset_flag = MCI_CNTRL_FIFO_RESET | MCI_CNTRL_DMA_RESET; + phytium_mci_adma_reset(host); + sdr_set_bits(host->base + MCI_CNTRL, reset_flag); + } else { + reset_flag = MCI_CNTRL_FIFO_RESET; + sdr_set_bits(host->base + MCI_CNTRL, reset_flag); + } + + while (readl(host->base + MCI_CNTRL) & reset_flag) + cpu_relax(); + + sdr_clr_bits(host->base + MCI_CARD_RESET, MCI_CARD_RESET_ENABLE); + udelay(5); + sdr_set_bits(host->base + MCI_CARD_RESET, MCI_CARD_RESET_ENABLE); + usleep_range(200, 300); +} + +#ifdef CONFIG_PM_SLEEP +int phytium_mci_suspend(struct device *dev) +{ + struct mmc_host *mmc = dev_get_drvdata(dev); + struct phytium_mci_host *host = mmc_priv(mmc); + + phytium_mci_deinit_hw(host); + return 0; +} +EXPORT_SYMBOL(phytium_mci_suspend); + +int phytium_mci_resume(struct device *dev) +{ + struct mmc_host *mmc = dev_get_drvdata(dev); + struct phytium_mci_host *host = mmc_priv(mmc); + + phytium_mci_init_hw(host); + return 0; +} +EXPORT_SYMBOL(phytium_mci_resume); + +#endif + +#ifdef CONFIG_PM +int phytium_mci_runtime_suspend(struct device *dev) +{ + struct mmc_host *mmc = dev_get_drvdata(dev); + struct phytium_mci_host *host = mmc_priv(mmc); + + phytium_mci_deinit_hw(host); + return 0; +} +EXPORT_SYMBOL(phytium_mci_runtime_suspend); + +int phytium_mci_runtime_resume(struct device *dev) +{ + struct mmc_host *mmc = dev_get_drvdata(dev); + struct phytium_mci_host *host = mmc_priv(mmc); + + phytium_mci_init_hw(host); + return 0; +} +EXPORT_SYMBOL(phytium_mci_runtime_resume); + +#endif + +static struct mmc_host_ops phytium_mci_ops = { + .post_req = phytium_mci_post_req, + .pre_req = phytium_mci_pre_req, + .request = phytium_mci_ops_request, + .set_ios = phytium_mci_ops_set_ios, + .get_cd = phytium_mci_get_cd, + .enable_sdio_irq = phytium_mci_enable_sdio_irq, + .ack_sdio_irq = phytium_mci_ack_sdio_irq, + .card_busy = phytium_mci_card_busy, + .start_signal_voltage_switch = phytium_mci_ops_switch_volt, + .hw_reset = phytium_mci_hw_reset, +}; + +int phytium_mci_common_probe(struct phytium_mci_host *host) +{ + struct mmc_host *mmc = host->mmc; + struct device *dev = host->dev; + int ret; + + dma_set_mask(dev, DMA_BIT_MASK(64)); + dma_set_coherent_mask(dev, DMA_BIT_MASK(64)); + + timer_setup(&host->hotplug_timer, hotplug_timer_func, 0); + timer_setup(&host->timeout_timer, phytium_mci_request_timeout, 0); + + mmc->f_min = MCI_F_MIN; + if (!mmc->f_max) + mmc->f_max = MCI_F_MAX; + + mmc->ops = &phytium_mci_ops; + mmc->ocr_avail_sdio = MMC_VDD_32_33 | MMC_VDD_33_34; + mmc->ocr_avail_sd = MMC_VDD_32_33 | MMC_VDD_33_34; + mmc->ocr_avail_mmc = MMC_VDD_165_195; + mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; + mmc->caps |= host->caps; + + if (mmc->caps & MMC_CAP_SDIO_IRQ) { + mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD; + dev_dbg(host->dev, "%s %d: MMC_CAP_SDIO_IRQ\n", __func__, __LINE__); + } + mmc->caps2 |= host->caps2; + if (host->is_use_dma) { + /* MMC core transfer sizes tunable parameters */ + mmc->max_segs = MAX_BD_NUM; + mmc->max_seg_size = 4 * 1024; + mmc->max_blk_size = 512; + mmc->max_req_size = 512 * 1024; + mmc->max_blk_count = mmc->max_req_size / 512; + host->dma.adma_table = dma_alloc_coherent(host->dev, + MAX_BD_NUM * + sizeof(struct phytium_adma2_64_desc), + &host->dma.adma_addr, GFP_KERNEL); + if (!host->dma.adma_table) + return MCI_REALEASE_MEM; + + host->dma.desc_sz = ADMA2_64_DESC_SZ; + phytium_mci_init_adma_table(host, &host->dma); + } else { + mmc->max_segs = MAX_BD_NUM; + mmc->max_seg_size = 4 * 1024; + mmc->max_blk_size = 512; + mmc->max_req_size = 4 * 512; + mmc->max_blk_count = mmc->max_req_size / 512; + } + + spin_lock_init(&host->lock); + + phytium_mci_init_hw(host); + ret = devm_request_irq(host->dev, host->irq, phytium_mci_irq, + host->irq_flags, "phytium-mci", host); + + if (ret) + return ret; + + ret = mmc_add_host(mmc); + + if (ret) { + dev_err(host->dev, "%s %d: mmc add host!\n", __func__, __LINE__); + return ret; + } + return 0; +} +EXPORT_SYMBOL(phytium_mci_common_probe); + +MODULE_DESCRIPTION("Phytium Multimedia Card Interface driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Cheng Quan "); diff --git a/target/linux/phytium/files-5.10/drivers/mmc/host/phytium-mci.h b/target/linux/phytium/files-5.10/drivers/mmc/host/phytium-mci.h new file mode 100644 index 000000000..5423597ec --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/mmc/host/phytium-mci.h @@ -0,0 +1,357 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Driver for Phytium Multimedia Card Interface + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#ifndef __PHYTIUM_MCI_H +#define __PHYTIUM_MCI_H + +#include +#include +#include +#include +#include +#include +#include + +/*------------------------------------------------------*/ +/* Common Definition */ +/*------------------------------------------------------*/ +#define MAX_BD_NUM 128 +#define SD_BLOCK_SIZE 512 + +#define MCI_BUS_1BITS 0x0 +#define MCI_BUS_4BITS 0x1 +#define MCI_BUS_8BITS (0x1 << 16) + +#define MCI_SD_DRV_VALUE 0 +#define MCI_SD_SAMP_VALUE_MAX 0 +#define MCI_SD_SAMP_VALUE_MIN 0 + +#define MCI_TIMEOUT_CMD_VALUE 0xFFFFFFFF +#define MCI_POWER_ON 1 +#define MCI_POWER_OFF 0 + +#define MCI_PREPARE_FLAG (0x1 << 0) +#define MCI_ASYNC_FLAG (0x1 << 1) +#define MCI_MMAP_FLAG (0x1 << 2) + +#define MCI_CMD_TIMEOUT (HZ/10 * 50) /* 100ms x5 */ +#define MCI_DATA_TIMEOUT (HZ * 10) /* 1000ms x5 */ + +#define MCI_CMD_TYPE_ADTC 0x2 + +#define MCI_F_MIN 400000 +#define MCI_F_MAX 50000000 + +#define MCI_CLK 1200000000 +#define MCI_REALEASE_MEM 0x1 +#define MCI_MAX_FIFO_CNT 0x800 + +/* FIFOTH register defines */ +#define MCI_SET_FIFOTH(m, r, t) (((m) & 0x7) << 28 | \ + ((r) & 0xFFF) << 16 | ((t) & 0xFFF)) +/* Card read threshold */ +#define MCI_SET_THLD(v, x) (((v) & 0xFFF) << 16 | (x)) +#define MCI_CARD_WR_THR_EN BIT(2) +#define MCI_CARD_RD_THR_EN BIT(0) + +/*----------------------------------------------------------------------*/ +/* Register Offset */ +/*----------------------------------------------------------------------*/ +#define MCI_CNTRL 0x00 /* the controller config reg */ +#define MCI_PWREN 0x04 /* the power enable reg */ +#define MCI_CLKDIV 0x08 /* the clock divider reg */ +#define MCI_CLKENA 0x10 /* the clock enable reg */ +#define MCI_TMOUT 0x14 /* the timeout reg */ +#define MCI_CTYPE 0x18 /* the card type reg */ +#define MCI_BLKSIZ 0x1C /* the block size reg */ +#define MCI_BYTCNT 0x20 /* the byte count reg */ +#define MCI_INT_MASK 0x24 /* the interrupt mask reg */ +#define MCI_CMDARG 0x28 /* the command argument reg */ +#define MCI_CMD 0x2C /* the command reg */ +#define MCI_RESP0 0x30 /* the response reg0 */ +#define MCI_RESP1 0x34 /* the response reg1 */ +#define MCI_RESP2 0x38 /* the response reg2 */ +#define MCI_RESP3 0X3C /* the response reg3 */ +#define MCI_MASKED_INTS 0x40 /* the masked interrupt status reg */ +#define MCI_RAW_INTS 0x44 /* the raw interrupt status reg */ +#define MCI_STATUS 0x48 /* the status reg */ +#define MCI_FIFOTH 0x4C /* the FIFO threshold watermark reg */ +#define MCI_CARD_DETECT 0x50 /* the card detect reg */ +#define MCI_CARD_WRTPRT 0x54 /* the card write protect reg */ +#define MCI_CCLK_RDY 0x58 /* first div is ready? 1:ready,0:not ready*/ +#define MCI_TRAN_CARD_CNT 0x5C /* the transferred CIU card byte count reg */ +#define MCI_TRAN_FIFO_CNT 0x60 /* the transferred host to FIFO byte count reg */ +#define MCI_DEBNCE 0x64 /* the debounce count reg */ +#define MCI_UID 0x68 /* the user ID reg */ +#define MCI_VID 0x6C /* the controller version ID reg */ +#define MCI_HWCONF 0x70 /* the hardware configuration reg */ +#define MCI_UHS_REG 0x74 /* the UHS-I reg */ +#define MCI_CARD_RESET 0x78 /* the card reset reg */ +#define MCI_BUS_MODE 0x80 /* the bus mode reg */ +#define MCI_DESC_LIST_ADDRL 0x88 /* the descriptor list low base address reg */ +#define MCI_DESC_LIST_ADDRH 0x8C /* the descriptor list high base address reg */ +#define MCI_DMAC_STATUS 0x90 /* the internal DMAC status reg */ +#define MCI_DMAC_INT_ENA 0x94 /* the internal DMAC interrupt enable reg */ +#define MCI_CUR_DESC_ADDRL 0x98 /* the current host descriptor low address reg */ +#define MCI_CUR_DESC_ADDRH 0x9C /* the current host descriptor high address reg */ +#define MCI_CUR_BUF_ADDRL 0xA0 /* the current buffer low address reg */ +#define MCI_CUR_BUF_ADDRH 0xA4 /* the current buffer high address reg */ +#define MCI_CARD_THRCTL 0x100 /* the card threshold control reg */ +#define MCI_UHS_REG_EXT 0x108 /* the UHS register extension */ +#define MCI_EMMC_DDR_REG 0x10C /* the EMMC DDR reg */ +#define MCI_ENABLE_SHIFT 0x110 /* the enable phase shift reg */ +#define MCI_DATA 0x200 /* the data FIFO access */ + +/* Command register defines */ +#define MCI_CMD_START BIT(31) +#define MCI_CMD_USE_HOLD_REG BIT(29) +#define MCI_CMD_VOLT_SWITCH BIT(28) +#define MCI_CMD_CCS_EXP BIT(23) +#define MCI_CMD_CEATA_RD BIT(22) +#define MCI_CMD_UPD_CLK BIT(21) +#define MCI_CMD_INIT BIT(15) +#define MCI_CMD_STOP BIT(14) +#define MCI_CMD_PRV_DAT_WAIT BIT(13) +#define MCI_CMD_SEND_STOP BIT(12) +#define MCI_CMD_STRM_MODE BIT(11) +#define MCI_CMD_DAT_WR BIT(10) +#define MCI_CMD_DAT_EXP BIT(9) +#define MCI_CMD_RESP_CRC BIT(8) +#define MCI_CMD_RESP_LONG BIT(7) +#define MCI_CMD_RESP_EXP BIT(6) +#define MCI_CMD_INDX(n) ((n) & 0x1F) + +/*------------------------------------------------------*/ +/* Register Mask */ +/*------------------------------------------------------*/ +/* MCI_CNTRL mask */ +#define MCI_CNTRL_CONTROLLER_RESET (0x1 << 0) /* RW */ +#define MCI_CNTRL_FIFO_RESET (0x1 << 1) /* RW */ +#define MCI_CNTRL_DMA_RESET (0x1 << 2) /* RW */ +#define MCI_CNTRL_RES (0x1 << 3) /* */ +#define MCI_CNTRL_INT_ENABLE (0x1 << 4) /* RW */ +#define MCI_CNTRL_DMA_ENABLE (0x1 << 5) /* RW */ +#define MCI_CNTRL_READ_WAIT (0x1 << 6) /* RW */ +#define MCI_CNTRL_SEND_IRQ_RESPONSE (0x1 << 7) /* RW */ +#define MCI_CNTRL_ABORT_READ_DATA (0x1 << 8) /* RW */ +#define MCI_CNTRL_ENDIAN (0x1 << 11) /* RW */ +//#define MCI_CNTRL_CARD_VOLTAGE_A (0xF << 16) /* RW */ +//#define MCI_CNTRL_CARD_VOLTAGE_B (0xF << 20) /* RW */ +#define MCI_CNTRL_ENABLE_OD_PULLUP (0x1 << 24) /* RW */ +#define MCI_CNTRL_USE_INTERNAL_DMAC (0x1 << 25) /* RW */ + +/* MCI_PWREN mask */ +#define MCI_PWREN_ENABLE (0x1 << 0) /* RW */ + +/* MCI_CLKENA mask */ +#define MCI_CLKENA_CCLK_ENABLE (0x1 << 0) /* RW */ +#define MCI_CLKENA_CCLK_LOW_POWER (0x1 << 16) /* RW */ +#define MCI_EXT_CLK_ENABLE (0x1 << 1) + +/* MCI_INT_MASK mask */ +#define MCI_INT_MASK_CD (0x1 << 0) /* RW */ +#define MCI_INT_MASK_RE (0x1 << 1) /* RW */ +#define MCI_INT_MASK_CMD (0x1 << 2) /* RW */ +#define MCI_INT_MASK_DTO (0x1 << 3) /* RW */ +#define MCI_INT_MASK_TXDR (0x1 << 4) /* RW */ +#define MCI_INT_MASK_RXDR (0x1 << 5) /* RW */ +#define MCI_INT_MASK_RCRC (0x1 << 6) /* RW */ +#define MCI_INT_MASK_DCRC (0x1 << 7) /* RW */ +#define MCI_INT_MASK_RTO (0x1 << 8) /* RW */ +#define MCI_INT_MASK_DRTO (0x1 << 9) /* RW */ +#define MCI_INT_MASK_HTO (0x1 << 10) /* RW */ +#define MCI_INT_MASK_FRUN (0x1 << 11) /* RW */ +#define MCI_INT_MASK_HLE (0x1 << 12) /* RW */ +#define MCI_INT_MASK_SBE_BCI (0x1 << 13) /* RW */ +#define MCI_INT_MASK_ACD (0x1 << 14) /* RW */ +#define MCI_INT_MASK_EBE (0x1 << 15) /* RW */ +#define MCI_INT_MASK_SDIO (0x1 << 16) /* RW */ + +/* MCI_MASKED_INTS mask */ +#define MCI_MASKED_INTS_CD (0x1 << 0) /* RO */ +#define MCI_MASKED_INTS_RE (0x1 << 1) /* RO */ +#define MCI_MASKED_INTS_CMD (0x1 << 2) /* RO */ +#define MCI_MASKED_INTS_DTO (0x1 << 3) /* RO */ +#define MCI_MASKED_INTS_TXDR (0x1 << 4) /* RO */ +#define MCI_MASKED_INTS_RXDR (0x1 << 5) /* RO */ +#define MCI_MASKED_INTS_RCRC (0x1 << 6) /* RO */ +#define MCI_MASKED_INTS_DCRC (0x1 << 7) /* RO */ +#define MCI_MASKED_INTS_RTO (0x1 << 8) /* RO */ +#define MCI_MASKED_INTS_DRTO (0x1 << 9) /* RO */ +#define MCI_MASKED_INTS_HTO (0x1 << 10) /* RO */ +#define MCI_MASKED_INTS_FRUN (0x1 << 11) /* RO */ +#define MCI_MASKED_INTS_HLE (0x1 << 12) /* RO */ +#define MCI_MASKED_INTS_SBE_BCI (0x1 << 13) /* RO */ +#define MCI_MASKED_INTS_ACD (0x1 << 14) /* RO */ +#define MCI_MASKED_INTS_EBE (0x1 << 15) /* RO */ +#define MCI_MASKED_INTS_SDIO (0x1 << 16) /* RO */ + +/* MCI_RAW_INTS mask */ +#define MCI_RAW_INTS_CD (0x1 << 0) /* W1C */ +#define MCI_RAW_INTS_RE (0x1 << 1) /* W1C */ +#define MCI_RAW_INTS_CMD (0x1 << 2) /* W1C */ +#define MCI_RAW_INTS_DTO (0x1 << 3) /* W1C */ +#define MCI_RAW_INTS_TXDR (0x1 << 4) /* W1C */ +#define MCI_RAW_INTS_RXDR (0x1 << 5) /* W1C */ +#define MCI_RAW_INTS_RCRC (0x1 << 6) /* W1C */ +#define MCI_RAW_INTS_DCRC (0x1 << 7) /* W1C */ +#define MCI_RAW_INTS_RTO (0x1 << 8) /* W1C */ +#define MCI_RAW_INTS_DRTO (0x1 << 9) /* W1C */ +#define MCI_RAW_INTS_HTO (0x1 << 10) /* W1C */ +#define MCI_RAW_INTS_FRUN (0x1 << 11) /* W1C */ +#define MCI_RAW_INTS_HLE (0x1 << 12) /* W1C */ +#define MCI_RAW_INTS_SBE_BCI (0x1 << 13) /* W1C */ +#define MCI_RAW_INTS_ACD (0x1 << 14) /* W1C */ +#define MCI_RAW_INTS_EBE (0x1 << 15) /* W1C */ +#define MCI_RAW_INTS_SDIO (0x1 << 16) /* W1C */ + +/* MCI_STATUS mask */ +#define MCI_STATUS_FIFO_RX (0x1 << 0) /* RO */ +#define MCI_STATUS_FIFO_TX (0x1 << 1) /* RO */ +#define MCI_STATUS_FIFO_EMPTY (0x1 << 2) /* RO */ +#define MCI_STATUS_FIFO_FULL (0x1 << 3) /* RO */ +#define MCI_STATUS_CARD_STATUS (0x1 << 8) /* RO */ +#define MCI_STATUS_CARD_BUSY (0x1 << 9) /* RO */ +#define MCI_STATUS_DATA_BUSY (0x1 << 10) /* RO */ +#define MCI_STATUS_RESPOSE_INDEX_OFFSET (11) +#define MCI_STATUS_RESPOSE_INDEX_MASK (0x3f << MCI_STATUS_RESPOSE_INDEX_OFFSET) /* RO */ +#define MCI_STATUS_RESPOSE_INDEX(reg) (((reg) & MCI_STATUS_RESPOSE_INDEX_MASK) >> MCI_STATUS_RESPOSE_INDEX_OFFSET) +#define MCI_STATUS_DMA_ACK (0x1 << 31) /* RO */ +#define MCI_STATUS_DMA_REQ (0x1 << 32) /* RO */ + +/* MCI_UHS_REG mask */ +#define MCI_UHS_REG_VOLT (0x1 << 0) /* RW */ +#define MCI_UHS_REG_DDR (0x1 << 16) /* RW */ + +/* MCI_CARD_RESET mask */ +#define MCI_CARD_RESET_ENABLE (0x1 << 0) /* RW */ + +/* MCI_BUS_MODE mask */ +#define MCI_BUS_MODE_SWR (0x1 << 0) /* RW */ +#define MCI_BUS_MODE_FB (0x1 << 1) /* RW */ +#define MCI_BUS_MODE_DE (0x1 << 7) /* RW */ + +/* MCI_DMAC_STATUS mask */ +#define MCI_DMAC_STATUS_TI (0x1 << 0) /* RW */ +#define MCI_DMAC_STATUS_RI (0x1 << 1) /* RW */ +#define MCI_DMAC_STATUS_FBE (0x1 << 2) /* RW */ +#define MCI_DMAC_STATUS_DU (0x1 << 4) /* RW */ +#define MCI_DMAC_STATUS_NIS (0x1 << 8) /* RW */ +#define MCI_DMAC_STATUS_AIS (0x1 << 9) /* RW */ + +/* MCI_DMAC_INT_ENA mask */ +#define MCI_DMAC_INT_ENA_TI (0x1 << 0) /* RW */ +#define MCI_DMAC_INT_ENA_RI (0x1 << 1) /* RW */ +#define MCI_DMAC_INT_ENA_FBE (0x1 << 2) /* RW */ +#define MCI_DMAC_INT_ENA_DU (0x1 << 4) /* RW */ +#define MCI_DMAC_INT_ENA_CES (0x1 << 5) /* RW */ +#define MCI_DMAC_INT_ENA_NIS (0x1 << 8) /* RW */ +#define MCI_DMAC_INT_ENA_AIS (0x1 << 9) /* RW */ + +/* MCI_CARD_THRCTL mask */ +#define MCI_CARD_THRCTL_CARDRD (0x1 << 0) /* RW */ +#define MCI_CARD_THRCTL_BUSY_CLR (0x1 << 1) /* RW */ +#define MCI_CARD_THRCTL_CARDWR (0x1 << 2) /* RW */ + +/* MCI_UHS_REG_EXT mask */ +#define MCI_UHS_REG_EXT_MMC_VOLT (0x1 << 0) /* RW */ +#define MCI_UHS_REG_EXT_CLK_ENA (0x1 << 1) /* RW */ + +/* MCI_EMMC_DDR_REG mask */ +#define MCI_EMMC_DDR_CYCLE (0x1 << 0) /* RW */ + +/*--------------------------------------*/ +/* Structure Type */ +/*--------------------------------------*/ +/* Maximum segments assuming a 512KiB maximum requisition */ +/* size and a minimum4KiB page size. */ +#define MCI_MAX_SEGS 128 +/* ADMA2 64-bit DMA descriptor size */ +#define ADMA2_64_DESC_SZ 32 + +/* mmc request timeout 5000ms */ +#define MMC_REQ_TIMEOUT_MS 5000 + +/* Each descriptor can transfer up to 4KB of data in chained mode */ +/*ADMA2 64-bit descriptor.*/ +struct phytium_adma2_64_desc { + u32 attribute; +#define IDMAC_DES0_DIC BIT(1) +#define IDMAC_DES0_LD BIT(2) +#define IDMAC_DES0_FD BIT(3) +#define IDMAC_DES0_CH BIT(4) +#define IDMAC_DES0_ER BIT(5) +#define IDMAC_DES0_CES BIT(30) +#define IDMAC_DES0_OWN BIT(31) + u32 NON1; + u32 len; + u32 NON2; + u32 addr_lo; /* Lower 32-bits of Buffer Address Pointer 1*/ + u32 addr_hi; /* Upper 32-bits of Buffer Address Pointer 1*/ + u32 desc_lo; /* Lower 32-bits of Next Descriptor Address */ + u32 desc_hi; /* Upper 32-bits of Next Descriptor Address */ +} __packed __aligned(4); + +struct phytium_mci_dma { + struct scatterlist *sg; /* I/O scatter list */ + /* ADMA descriptor table, pointer to adma_table array */ + struct phytium_adma2_64_desc *adma_table; + /* Mapped ADMA descr. table, the physical address of adma_table array */ + dma_addr_t adma_addr; + unsigned int desc_sz; /* ADMA descriptor size */ +}; + +enum adtc_t { + COMMOM_ADTC = 0, + BLOCK_RW_ADTC = 1 +}; + +struct phytium_mci_host { + struct device *dev; + struct mmc_host *mmc; + u32 caps; + u32 caps2; + spinlock_t lock; + struct mmc_request *mrq; + struct mmc_command *cmd; + struct mmc_data *data; + int error; + void __iomem *base; /* host base address */ + void *adma_table1; + dma_addr_t adma_addr1; + struct phytium_mci_dma dma_rx; /* dma channel */ + struct phytium_mci_dma dma_tx; /* dma channel */ + struct phytium_mci_dma dma; /* dma channel */ + u64 dma_mask; + bool vqmmc_enabled; + u32 *sg_virt_addr; + enum adtc_t adtc_type; /* 0:common adtc cmd; 1:block r/w adtc cmd;*/ + struct timer_list hotplug_timer; + struct timer_list timeout_timer; + struct delayed_work req_timeout; + int irq; /* host interrupt */ + u32 current_rca; /*the current rca value*/ + u32 current_ios_clk; + u32 is_use_dma; + u32 is_device_x100; + struct clk *src_clk; /* phytium_mci source clock */ + unsigned long clk_rate; + unsigned long clk_div; + unsigned long irq_flags; + unsigned long flags; +#define MCI_CARD_NEED_INIT 1 + +}; + +int phytium_mci_common_probe(struct phytium_mci_host *host); +void phytium_mci_deinit_hw(struct phytium_mci_host *host); +int phytium_mci_runtime_suspend(struct device *dev); +int phytium_mci_runtime_resume(struct device *dev); +int phytium_mci_resume(struct device *dev); +int phytium_mci_suspend(struct device *dev); + +#endif /* __PHYTIUM_MCI_HW_H */ diff --git a/target/linux/phytium/files-5.10/drivers/mmc/host/phytium-sdci.c b/target/linux/phytium/files-5.10/drivers/mmc/host/phytium-sdci.c new file mode 100755 index 000000000..929162b8d --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/mmc/host/phytium-sdci.c @@ -0,0 +1,1433 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Phytium SD Card Interface dirver + * + * Copyright (c) 2019-2023, Phytium Technology Co.,Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "phytium-sdci.h" + +static const u32 cmd_ints_mask = SDCI_SDCI_NORMAL_ISER_ECC_EN | SDCI_SDCI_NORMAL_ISER_EEI_EN; +static const u32 data_ints_mask = SDCI_BD_ISER_ETRS_EN; +static const u32 err_ints_mask = SDCI_ERROR_ISER_ECTE_EN | SDCI_ERROR_ISR_CCRCE_EN | + SDCI_ERROR_ISR_CIR_EN | SDCI_ERROR_ISR_CNR_EN; + +static void hotplug_timer_func(struct timer_list *t); +static bool phytium_sdci_private_send_cmd(struct phytium_sdci_host *host, + u32 cmd, u32 resp_type, u32 arg); +static bool phytium_sdci_cmd_done(struct phytium_sdci_host *host, int events, + struct mmc_request *mrq, + struct mmc_command *cmd); +static bool phytium_sdci_data_xfer_done(struct phytium_sdci_host *host, + u32 events, struct mmc_request *mrq, + struct mmc_data *data); +static void phytium_sdci_cmd_next(struct phytium_sdci_host *host, + struct mmc_request *mrq, + struct mmc_command *cmd); + +static int phytium_sdci_cmd13_process(struct phytium_sdci_host *host, + struct mmc_request *mrq, + struct mmc_data *data, + u32 wait_timeout_ms, + u32 send_once_time_ms); + +static int phytium_sd_error(struct phytium_sdci_host *host) +{ + int temp; + + temp = readl(host->base + SDCI_NORMAL_ISR); + dev_err(host->dev, "[%s %d]SDCI_NORMAL_ISR:%x\n", __func__, __LINE__, temp); + temp = readl(host->base + SDCI_BD_ISR); + temp = readl(host->base + SDCI_ERROR_ISR); + dev_err(host->dev, "[%s %d]SDCI_ERROR_ISR:%x\n", __func__, __LINE__, temp); + temp = readl(host->base + SDCI_BD_ISR); + dev_err(host->dev, "[%s %d]SDCI_BD_ISR:%x\n", __func__, __LINE__, temp); + temp = readl(host->base + SDCI_RESP0); + dev_err(host->dev, "[%s %d]SDCI_RESP0:%x\n", __func__, __LINE__, temp); + + return 0; +} + +static void sdr_set_bits(void __iomem *reg, u32 bs) +{ + u32 val; + + val = readl(reg); + val |= bs; + + writel(val, reg); +} + +static void sdr_clr_bits(void __iomem *reg, u32 bs) +{ + u32 val; + + val = readl(reg); + val &= ~bs; + + writel(val, reg); +} + +static void phytium_sdci_reset_hw(struct phytium_sdci_host *host) +{ + sdr_set_bits(host->base + SDCI_SOFTWARE, + SDCI_SOFTWARE_SRST); + sdr_clr_bits(host->base + SDCI_SOFTWARE, + SDCI_SOFTWARE_SRST); + while (!(readl(host->base + SDCI_STATUS) & SDCI_STATUS_IDIE)) + cpu_relax(); +} + +static void phytium_sdci_prepare_data(struct phytium_sdci_host *host, + struct mmc_request *mrq) +{ + struct mmc_data *data = mrq->data; + bool read; + + read = (data->flags & MMC_DATA_READ) != 0; + data->sg_count = dma_map_sg(host->dev, data->sg, data->sg_len, + read ? DMA_FROM_DEVICE : DMA_TO_DEVICE); +} + +static void phytium_sdci_unprepare_data(struct phytium_sdci_host *host, + struct mmc_request *mrq) +{ + bool read; + struct mmc_data *data = mrq->data; + + read = (data->flags & MMC_DATA_READ) != 0; + dma_unmap_sg(host->dev, data->sg, data->sg_len, + read ? DMA_FROM_DEVICE : DMA_TO_DEVICE); +} + +static void phytium_sdci_set_clk(struct phytium_sdci_host *host, + struct mmc_ios *ios) +{ + unsigned long clk_rate; + u32 div = 0xffffffff, div_reg; + + if (ios->clock) { + clk_rate = host->clk_rate; + div = ((clk_rate / (2 * ios->clock)) - 1); + div_reg = readl(host->base + SDCI_CLOCK_D); + if (div_reg == div) + return; + writel(div, host->base + SDCI_CLOCK_D); + writel(0, host->base + SDCI_SD_DRV); + writel(5, host->base + SDCI_SD_SAMP); + + sdr_set_bits(host->base + SDCI_SOFTWARE, SDCI_SOFTWARE_SRST); + sdr_clr_bits(host->base + SDCI_SOFTWARE, SDCI_SOFTWARE_SRST); + while (!(readl(host->base + SDCI_STATUS) & SDCI_STATUS_IDIE)) + cpu_relax(); + dev_dbg(host->dev, "host->clk_rate: %ld, ios->clock: %d\n", + host->clk_rate, ios->clock); + } +} + + +static inline u32 phytium_sdci_cmd_find_resp(struct phytium_sdci_host *host, + struct mmc_request *mrq, + struct mmc_command *cmd) +{ + u32 resp; + + switch (mmc_resp_type(cmd)) { + case MMC_RSP_R1: + resp = 0x2; + break; + case MMC_RSP_R1B: + resp = 0x2; + break; + case MMC_RSP_R2: + resp = 0x1; + break; + case MMC_RSP_R3: + resp = 0x3; + break; + case MMC_RSP_NONE: + default: + resp = 0x0; + break; + } + + return resp; +} + +static inline u32 phytium_sdci_cmd_prepare_raw_cmd(struct phytium_sdci_host *host, + struct mmc_request *mrq, struct mmc_command *cmd) +{ + /* + * rawcmd : + * trty << 14 | opcode << 8 | cmdw << 6 | cice << 4 | crce << 3 | resp + */ + u32 resp, rawcmd; + u32 opcode = cmd->opcode; + + resp = phytium_sdci_cmd_find_resp(host, mrq, cmd); + rawcmd = ((opcode << 8) | resp); + + if ((cmd->flags & MMC_CMD_MASK) == MMC_CMD_ADTC) + rawcmd = (rawcmd | (SDCI_CMD_TYPE_ADTC << 14)); + + return rawcmd; +} + +static void +phytium_sdci_unexpected_error_handler(struct phytium_sdci_host *host, + struct mmc_request *mrq, + struct mmc_data *data, + int err_type) +{ + unsigned long flags; + int error; + + spin_lock_irqsave(&host->lock, flags); + host->mrq = NULL; + host->cmd = NULL; + host->data = NULL; + spin_unlock_irqrestore(&host->lock, flags); + + if (err_type & ERR_CARD_ABSENT) { + host->mmc->detect_change = 1; + dev_dbg(host->dev, "SD is absent when send cmd:%d\n", mrq->cmd->opcode); + } + + switch (err_type) { + case ERR_CARD_ABSENT: + error = -ENOMEDIUM; + break; + case ERR_TIMEOUT: + error = -ETIMEDOUT; + break; + case ERR_CMD_RESPONED: + error = -EIO; + break; + default: + error = -ETIMEDOUT; + break; + } + + if (data) { + data->error = error; + phytium_sdci_unprepare_data(host, mrq); + + if ((data->flags & MMC_DATA_READ) == MMC_DATA_READ || + (data->flags & MMC_DATA_WRITE) == MMC_DATA_WRITE) + phytium_sdci_data_xfer_done(host, SDCI_BD_ISR_TRS_R, mrq, data); + } else { + mrq->cmd->error = error; + } + + mmc_request_done(host->mmc, mrq); +} + +static bool phytium_sdci_start_data(struct phytium_sdci_host *host, struct mmc_request *mrq, + struct mmc_command *cmd, struct mmc_data *data) +{ + bool read, res; + u32 sg_dma_addrh, sg_dma_addrl; + u32 sd_block_addrh, sd_block_addrl; + u32 temp, timeout, sd_status; + u32 block_cnt = 0; + u32 sd_block_addr = cmd->arg; + u32 private_cmd, resp_type, arg; + u32 j, dma_len; + unsigned long deadline_time; + dma_addr_t dma_address; + struct scatterlist *sg; + int ret; + + WARN_ON(host->cmd); + host->cmd = cmd; + + WARN_ON(host->data); + host->data = data; + read = data->flags & MMC_DATA_READ; + + for_each_sg(data->sg, sg, data->sg_count, j) { + writel(0, host->base + SDCI_COMMAND); + + dma_address = sg_dma_address(sg); + sg_dma_addrh = (u32) (dma_address >> 32); + sg_dma_addrl = (u32) dma_address; + + dma_len = sg_dma_len(sg); + block_cnt = (dma_len / SD_BLOCK_SIZE); + + sd_block_addrh = 0; + sd_block_addrl = sd_block_addr; + + sdr_set_bits(host->base + SDCI_SOFTWARE, SDCI_SOFTWARE_BDRST); + sdr_clr_bits(host->base + SDCI_SOFTWARE, SDCI_SOFTWARE_BDRST); + writel(block_cnt, host->base + SDCI_BLK_CNT); + + if ((mrq->data->flags & MMC_DATA_READ) == MMC_DATA_READ) { + writel(sg_dma_addrl, host->base + SDCI_BD_RX); + writel(sg_dma_addrh, host->base + SDCI_BD_RX); + writel(sd_block_addrl, host->base + SDCI_BD_RX); + writel(sd_block_addrh, host->base + SDCI_BD_RX); + timeout = 100 * block_cnt; + } else { + timeout = 250 * block_cnt; + ret = phytium_sdci_cmd13_process(host, mrq, data, timeout, 1); + if (ret != SDCI_CMD13_OK) + return false; + + writel(sg_dma_addrl, host->base + SDCI_BD_TX); + writel(sg_dma_addrh, host->base + SDCI_BD_TX); + writel(sd_block_addrl, host->base + SDCI_BD_TX); + writel(sd_block_addrh, host->base + SDCI_BD_TX); + } + + deadline_time = jiffies + msecs_to_jiffies(timeout); + + temp = readl(host->base + SDCI_BD_ISR); + if ((mrq->data->flags & MMC_DATA_READ) == MMC_DATA_READ) { + while ((temp & SDCI_BD_ISR_TRS_R) != SDCI_BD_ISR_TRS_R) { + sd_status = readl(host->base + SDCI_STATUS); + if (sd_status & SDCI_STATUS_CDSL) { + phytium_sdci_unexpected_error_handler(host, mrq, data, + ERR_CARD_ABSENT); + if (temp & SDCI_BD_ISR_DAIS) + writel(1, host->base + SDCI_BD_ISR); + return false; + } + + temp = readl(host->base + SDCI_BD_ISR); + if (time_after(jiffies, deadline_time)) { + phytium_sdci_unexpected_error_handler(host, mrq, data, + ERR_TIMEOUT); + dev_err(host->dev, + "Read Data timeout:jiffies:0x%lx,dt_jiffies: 0x%lx, BD_isr_reg:0x%x,cmd:%d, REG_D0:0x%x\n", + jiffies, jiffies - deadline_time, temp, + cmd->opcode, readl(host->base + SDCI_STATUS)); + + return false; + } + } + } else { + while ((temp & SDCI_BD_ISR_TRS_W) != SDCI_BD_ISR_TRS_W) { + sd_status = readl(host->base + SDCI_STATUS); + if (sd_status & SDCI_STATUS_CDSL) { + phytium_sdci_unexpected_error_handler(host, mrq, data, + ERR_CARD_ABSENT); + dev_err(host->dev, "[%s][%d]: Card absent ! cmd(%d)\n", + __func__, __LINE__, mrq->cmd->opcode); + return false; + } + + temp = readl(host->base + SDCI_BD_ISR); + if (time_after(jiffies, deadline_time)) { + phytium_sdci_unexpected_error_handler(host, mrq, data, + ERR_TIMEOUT); + dev_err(host->dev, + "Write Date timeout: jiffies:0x%lx,dt_jiffies: 0x%lx,BD_isr_reg:0x%x\n", + jiffies, jiffies - deadline_time, temp); + return false; + } + } + } + writel(1, host->base + SDCI_BD_ISR); + writel(1, host->base + SDCI_NORMAL_ISR); + sd_block_addr = sd_block_addr + block_cnt; + + if (j < (data->sg_count - 1) && 1 < block_cnt) { + private_cmd = MMC_STOP_TRANSMISSION; + resp_type = 0x2; + arg = 0; + res = phytium_sdci_private_send_cmd(host, private_cmd, + resp_type, arg); + if (!res) { + sd_status = readl(host->base + SDCI_STATUS); + if (sd_status & SDCI_STATUS_CDSL) { + phytium_sdci_unexpected_error_handler(host, mrq, data, + ERR_CARD_ABSENT); + writel(1, host->base + SDCI_BD_ISR); + dev_err(host->dev, + "[%s][%d]:Card absent ! private_cmd(%d)\n", + __func__, __LINE__, private_cmd); + } else { + phytium_sdci_unexpected_error_handler(host, mrq, data, + ERR_CMD_RESPONED); + dev_err(host->dev, + "[%s][%d] cmd(%d) response errored\n", + __func__, __LINE__, mrq->cmd->opcode); + phytium_sd_error(host); + } + writel(1, host->base + SDCI_NORMAL_ISR); + return false; + } + writel(1, host->base + SDCI_NORMAL_ISR); + } + } + + host->is_multi_rw_only_one_blkcnt = false; + + if ((cmd->opcode == MMC_READ_MULTIPLE_BLOCK && block_cnt == 1) || + (cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK && block_cnt == 1)) + host->is_multi_rw_only_one_blkcnt = true; + + phytium_sdci_cmd_done(host, SDCI_NORMAL_ISR_CC, mrq, cmd); + if ((mrq->data->flags & MMC_DATA_READ) == MMC_DATA_READ) + phytium_sdci_data_xfer_done(host, SDCI_BD_ISR_TRS_R, + mrq, data); + else + phytium_sdci_data_xfer_done(host, SDCI_BD_ISR_TRS_W, + mrq, data); + + return true; +} + +static int phytium_sdci_auto_cmd_done(struct phytium_sdci_host *host, + int events, struct mmc_command *cmd) +{ + u32 *rsp = cmd->resp; + + rsp[0] = readl(host->base + SDCI_RESP0); + + if (events & SDCI_NORMAL_ISR_CC) + cmd->error = 0; + else { + phytium_sdci_reset_hw(host); + dev_err(host->dev, + "%s: AUTO_CMD%d arg=%08X; rsp %08X; cmd_error=%d\n", + __func__, cmd->opcode, cmd->arg, rsp[0], cmd->error); + } + + return cmd->error; +} + +static void phytium_sdci_track_cmd_data(struct phytium_sdci_host *host, + struct mmc_command *cmd, + struct mmc_data *data) +{ + if (host->error) + dev_dbg(host->dev, "%s: cmd=%d arg=%08X; host->error=0x%08X\n", + __func__, cmd->opcode, cmd->arg, host->error); +} + +static void phytium_sdci_request_done(struct phytium_sdci_host *host, + struct mmc_request *mrq) +{ + unsigned long flags; + + dev_dbg(host->dev, + "%s_%d:mrq->cmd->opcode:%d, mrq->cmd->arg:0x%x resp 0x%x 0x%x 0x%x 0x%x\n", + __func__, __LINE__, mrq->cmd->opcode, mrq->cmd->arg, + mrq->cmd->resp[0], mrq->cmd->resp[1], mrq->cmd->resp[2], + mrq->cmd->resp[3]); + + spin_lock_irqsave(&host->lock, flags); + host->mrq = NULL; + spin_unlock_irqrestore(&host->lock, flags); + + phytium_sdci_track_cmd_data(host, mrq->cmd, mrq->data); + if (mrq->data) + phytium_sdci_unprepare_data(host, mrq); + mmc_request_done(host->mmc, mrq); +} + +static bool +phytium_sdci_auto_command_done(struct phytium_sdci_host *host, int events, + struct mmc_request *mrq, struct mmc_command *cmd) +{ + u32 *rsp = cmd->resp; + unsigned long flags; + + spin_lock_irqsave(&host->lock, flags); + host->cmd = NULL; + spin_unlock_irqrestore(&host->lock, flags); + + sdr_clr_bits(host->base + SDCI_NORMAL_ISER, cmd_ints_mask); + + rsp[0] = 0x900; + phytium_sdci_request_done(host, mrq); + return true; +} + +/* returns true if command is fully handled; returns false otherwise */ +static bool phytium_sdci_cmd_done(struct phytium_sdci_host *host, int events, + struct mmc_request *mrq, + struct mmc_command *cmd) +{ + bool done = false; + bool sbc_error; + unsigned long flags; + u32 *rsp = cmd->resp; + + if (mrq->sbc && cmd == mrq->cmd && + (events & SDCI_NORMAL_ISR_CC)) + phytium_sdci_auto_cmd_done(host, events, mrq->sbc); + + sbc_error = mrq->sbc && mrq->sbc->error; + + if (!sbc_error && !(events & (SDCI_NORMAL_ISR_CC | + SDCI_NORMAL_ISR_CR | + SDCI_NORMAL_ISR_TIMEOUT))) + return done; + + spin_lock_irqsave(&host->lock, flags); + done = !host->cmd; + host->cmd = NULL; + spin_unlock_irqrestore(&host->lock, flags); + + if (done) + return true; + + sdr_clr_bits(host->base + SDCI_NORMAL_ISER, cmd_ints_mask); + + if (cmd->flags & MMC_RSP_PRESENT) { + if (cmd->flags & MMC_RSP_136) { + rsp[0] = readl(host->base + SDCI_RESP0); + rsp[1] = readl(host->base + SDCI_RESP1); + rsp[2] = readl(host->base + SDCI_RESP2); + rsp[3] = readl(host->base + SDCI_RESP3); + } else + rsp[0] = readl(host->base + SDCI_RESP0); + + if (cmd->opcode == SD_SEND_RELATIVE_ADDR) + host->current_rca = rsp[0] & 0xFFFF0000; + } + + if (!sbc_error && + !(events & SDCI_NORMAL_ISR_CC) && + (events & SDCI_NORMAL_ISR_TIMEOUT)) + cmd->error = -ETIMEDOUT; + + if (cmd->error) + dev_dbg(host->dev, + "%s: cmd=%d arg=%08X; rsp %08X; cmd_error=%d\n", + __func__, cmd->opcode, cmd->arg, rsp[0], + cmd->error); + + phytium_sdci_cmd_next(host, mrq, cmd); + + return true; +} + +static bool set_databus_width(struct phytium_sdci_host *host) +{ + bool res; + u32 cmd, resp_type, arg; + + cmd = SD_APP_SET_BUS_WIDTH; + resp_type = 0x2; + arg = 0x2; + res = phytium_sdci_private_send_cmd(host, cmd, resp_type, arg); + if (!res) + return false; + + cmd = MMC_APP_CMD; + resp_type = 0x2; + arg = host->current_rca; + res = phytium_sdci_private_send_cmd(host, cmd, resp_type, arg); + if (!res) + return false; + + return true; +} + + +static void phytium_sdci_start_command(struct phytium_sdci_host *host, + struct mmc_request *mrq, + struct mmc_command *cmd) +{ + u32 rawcmd; + struct mmc_data *data = mrq->data; + dma_addr_t dma_adtc_buf; + u32 dma_bufh, dma_bufl, block_cnt = 0; + + WARN_ON(host->cmd); + host->cmd = cmd; + + cmd->error = 0; + rawcmd = phytium_sdci_cmd_prepare_raw_cmd(host, mrq, cmd); + if (cmd->opcode == MMC_STOP_TRANSMISSION || + cmd->opcode == MMC_SEND_STATUS) + writel(1, host->base + SDCI_ERROR_ISR); + sdr_set_bits(host->base + SDCI_NORMAL_ISER, cmd_ints_mask); + writel(rawcmd, host->base + SDCI_COMMAND); + + if ((cmd->flags & MMC_CMD_MASK) == MMC_CMD_ADTC) { + WARN_ON(host->data); + host->data = data; + + dma_adtc_buf = host->dma_rx.bd_addr; + dma_bufh = (u32) (dma_adtc_buf >> 32); + dma_bufl = (u32) dma_adtc_buf; + block_cnt = mrq->data->blocks; + sdr_set_bits(host->base + SDCI_BD_ISER, data_ints_mask); + writel(block_cnt, host->base + SDCI_BLK_CNT); + + if ((mrq->data->flags & MMC_DATA_READ) == MMC_DATA_READ) { + writel(dma_bufl, host->base + SDCI_BD_RX); + writel(dma_bufh, host->base + SDCI_BD_RX); + writel(cmd->arg, host->base + SDCI_BD_RX); + writel(0, host->base + SDCI_BD_RX); + } else { + writel(dma_bufl, host->base + SDCI_BD_TX); + writel(dma_bufh, host->base + SDCI_BD_TX); + writel(cmd->arg, host->base + SDCI_BD_TX); + writel(0, host->base + SDCI_BD_TX); + } + } else { + writel(cmd->arg, host->base + SDCI_ARGUMENT); + } +} + +static void phytium_sdci_cmd_next(struct phytium_sdci_host *host, + struct mmc_request *mrq, + struct mmc_command *cmd) +{ + if (cmd->error || (mrq->sbc && mrq->sbc->error)) + phytium_sdci_request_done(host, mrq); + else if (cmd == mrq->sbc) + phytium_sdci_start_command(host, mrq, mrq->cmd); + else if (!cmd->data) + phytium_sdci_request_done(host, mrq); +} + +static int phytium_sdci_cmd13_process(struct phytium_sdci_host *host, + struct mmc_request *mrq, + struct mmc_data *data, + u32 wait_timeout_ms, + u32 send_once_time_ms) +{ + u32 private_cmd, resp_type, arg, temp, sd_status; + unsigned long deadline_time; + bool res; + + deadline_time = jiffies + msecs_to_jiffies(wait_timeout_ms); + + do { + private_cmd = MMC_SEND_STATUS; + resp_type = 0x2; + arg = host->current_rca; + + res = phytium_sdci_private_send_cmd(host, private_cmd, resp_type, arg); + if (!res) { + sd_status = readl(host->base + SDCI_STATUS); + if (sd_status & SDCI_STATUS_CDSL) { + phytium_sdci_unexpected_error_handler(host, mrq, data, + ERR_CARD_ABSENT); + dev_err(host->dev, + "[%s][%d] Card absent! private_cmd(%d)\n", + __func__, __LINE__, private_cmd); + } else { + phytium_sdci_unexpected_error_handler(host, mrq, data, + ERR_CMD_RESPONED); + + dev_err(host->dev, + "[%s][%d] private_cmd(%d) response errored\n", + __func__, __LINE__, private_cmd); + phytium_sd_error(host); + } + writel(1, host->base + SDCI_BD_ISR); + return SDCI_CMD13_FAILED; + } + + temp = readl(host->base + SDCI_RESP0); + + if (time_after(jiffies, deadline_time)) { + + if (mrq->cmd->opcode == MMC_SEND_STATUS) + return SDCI_CMD13_OK; + + dev_err(host->dev, + "SD card is not in transfer mode,timeout:%d,rsp[0]:%x\n", + wait_timeout_ms, temp); + + phytium_sdci_unexpected_error_handler(host, mrq, data, + ERR_TIMEOUT); + phytium_sd_error(host); + return SDCI_CMD13_FAILED; + } + + writel(1, host->base + SDCI_NORMAL_ISR); + + if (CARD_TRAN_STATE != (temp & CARD_CURRENT_STATE) && send_once_time_ms) + mdelay(send_once_time_ms); + + } while (CARD_TRAN_STATE != (temp & CARD_CURRENT_STATE)); + + return SDCI_CMD13_OK; +} + +static void phytium_sdci_ops_request(struct mmc_host *mmc, + struct mmc_request *mrq) +{ + struct phytium_sdci_host *host = mmc_priv(mmc); + unsigned long flags; + bool res; + u32 status_sd; + int res_cmd13; + + host->error = 0; + WARN_ON(host->mrq); + host->mrq = mrq; + + dev_dbg(host->dev, "%s: mrq->cmd->opcode: %d, mrq->cmd->arg: 0x%x\n", + __func__, mrq->cmd->opcode, mrq->cmd->arg); + + if (mrq->cmd->opcode == MMC_SEND_STATUS && + (mrq->cmd->flags & MMC_CMD_MASK) != MMC_CMD_ADTC) { + u32 status = readl(host->base + SDCI_STATUS); + + if (status & SDCI_STATUS_CDSL) { + phytium_sdci_unexpected_error_handler(host, mrq, NULL, + ERR_CARD_ABSENT); + return; + } + + res_cmd13 = phytium_sdci_cmd13_process(host, mrq, NULL, 400, 5); + if (res_cmd13 == SDCI_CMD13_FAILED) + return; + } else if (mrq->cmd->opcode == MMC_STOP_TRANSMISSION) { + status_sd = readl(host->base + SDCI_STATUS); + if (status_sd & SDCI_STATUS_CDSL) { + phytium_sdci_unexpected_error_handler(host, mrq, NULL, + ERR_CARD_ABSENT); + return; + } + } + + if (mrq->data) { + phytium_sdci_prepare_data(host, mrq); + if (mrq->cmd->opcode == MMC_READ_MULTIPLE_BLOCK || + mrq->cmd->opcode == MMC_READ_SINGLE_BLOCK || + mrq->cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK || + mrq->cmd->opcode == MMC_WRITE_BLOCK) { + host->adtc_type = BLOCK_RW_ADTC; + phytium_sdci_start_data(host, mrq, + mrq->cmd, mrq->data); + return; + } + host->adtc_type = COMMOM_ADTC; + } + + if (mrq->cmd->opcode == SD_IO_RW_DIRECT || + mrq->cmd->opcode == SD_IO_SEND_OP_COND) { + spin_lock_irqsave(&host->lock, flags); + host->mrq = NULL; + host->cmd = NULL; + spin_unlock_irqrestore(&host->lock, flags); + mrq->cmd->error = -EINVAL; + mmc_request_done(host->mmc, mrq); + + return; + } + + if (mrq->cmd->opcode == SD_APP_SEND_SCR) { + res = set_databus_width(host); + if (!res) { + phytium_sdci_unexpected_error_handler(host, mrq, NULL, ERR_CMD_RESPONED); + return; + } + } + + /* if SBC is required, we have HW option and SW option. + * if HW option is enabled, and SBC does not have "special" flags, + * use HW option, otherwise use SW option + */ + if (mrq->sbc && + (!mmc_card_mmc(mmc->card) || (mrq->sbc->arg & 0xFFFF0000))) + phytium_sdci_start_command(host, mrq, mrq->sbc); + else + phytium_sdci_start_command(host, mrq, mrq->cmd); +} + +static void phytium_sdci_data_xfer_next(struct phytium_sdci_host *host, + struct mmc_request *mrq, + struct mmc_data *data) +{ + if (mmc_op_multi(mrq->cmd->opcode) && + mrq->stop && !mrq->stop->error && + !mrq->sbc && host->is_multi_rw_only_one_blkcnt) { + host->is_multi_rw_only_one_blkcnt = false; + phytium_sdci_auto_command_done(host, SDCI_NORMAL_ISR_CC, mrq, mrq->stop); + } else if (mmc_op_multi(mrq->cmd->opcode) && + mrq->stop && !mrq->stop->error && + !mrq->sbc) + phytium_sdci_start_command(host, mrq, mrq->stop); + else + phytium_sdci_request_done(host, mrq); +} + +static inline void get_data_buffer(struct mmc_data *data, + u32 *bytes, u32 **pointer) +{ + struct scatterlist *sg; + + sg = &data->sg[0]; + *bytes = sg->length; + *pointer = sg_virt(sg); +} + +static bool phytium_sdci_data_xfer_done(struct phytium_sdci_host *host, + u32 events, struct mmc_request *mrq, + struct mmc_data *data) +{ + struct mmc_command *stop = data->stop; + unsigned long flags; + bool done; + unsigned int check_data; + u32 sg_length, i; + u32 *sg_virt_addr; + + check_data = events & (SDCI_BD_ISR_TRS_R | SDCI_BD_ISR_TRS_W | SDCI_BD_ISR_EDTE); + + spin_lock_irqsave(&host->lock, flags); + done = !host->data; + if (check_data) + host->data = NULL; + spin_unlock_irqrestore(&host->lock, flags); + + if (done) + return true; + + if (check_data || (stop && stop->error)) { + sdr_clr_bits(host->base + SDCI_BD_ISER, data_ints_mask); + dev_dbg(host->dev, "DMA stop\n"); + + if (((events & SDCI_BD_ISR_TRS_R) || + (events & SDCI_BD_ISR_TRS_W)) && + (!stop || !stop->error)) { + if ((mrq->cmd->flags & MMC_CMD_MASK) == MMC_CMD_ADTC && + (host->adtc_type == COMMOM_ADTC)) { + get_data_buffer(data, &sg_length, + &host->sg_virt_addr); + sg_virt_addr = host->sg_virt_addr; + + for (i = 0; i < (sg_length/4); i++) { + *sg_virt_addr = host->dma_rx.buf[i]; + sg_virt_addr++; + } + } + data->bytes_xfered = data->blocks * data->blksz; + } else { + dev_dbg(host->dev, "interrupt events: %x\n", events); + phytium_sdci_reset_hw(host); + data->bytes_xfered = 0; + dev_dbg(host->dev, "%s: cmd=%d; blocks=%d", + __func__, mrq->cmd->opcode, data->blocks); + dev_dbg(host->dev, "data_error=%d xfer_size=%d\n", + (int)data->error, data->bytes_xfered); + } + + phytium_sdci_data_xfer_next(host, mrq, data); + done = true; + } + + return done; +} + + +static int phytium_sdci_card_busy(struct mmc_host *mmc) +{ + struct phytium_sdci_host *host = mmc_priv(mmc); + u32 status; + + /* check if any pin between dat[0:3] is low */ + status = readl(host->base + SDCI_STATUS); + if (((status >> 20) & 0xf) != 0xf) + return 1; + + return 0; +} + +static void phytium_sdci_request_timeout(struct work_struct *work) +{ + struct phytium_sdci_host *host; + + host = container_of(work, struct phytium_sdci_host, req_timeout.work); + dev_err(host->dev, "%s: aborting cmd/data/mrq\n", __func__); + if (host->mrq) { + dev_err(host->dev, "%s: aborting mrq=%p cmd=%d\n", __func__, + host->mrq, host->mrq->cmd->opcode); + if (host->cmd) { + dev_err(host->dev, "%s: aborting cmd=%d\n", + __func__, host->cmd->opcode); + phytium_sdci_cmd_done(host, SDCI_NORMAL_ISR_TIMEOUT, + host->mrq, host->cmd); + } else if (host->data) { + dev_err(host->dev, "%s: abort data: cmd%d; %d blocks\n", + __func__, host->mrq->cmd->opcode, + host->data->blocks); + phytium_sdci_data_xfer_done(host, SDCI_BD_ISR_EDTE, + host->mrq, host->data); + } + } +} + +static void hotplug_timer_func(struct timer_list *t) +{ + struct phytium_sdci_host *host; + u32 status; + + host = from_timer(host, t, hotplug_timer); + if (!host) + dev_err(host->dev, "%s: Not find host!\n", __func__); + status = readl(host->base + SDCI_STATUS); + + if (status & SDCI_STATUS_CDSL) { /* card absent */ + if (host->mmc->card) { + cancel_delayed_work(&host->mmc->detect); + mmc_detect_change(host->mmc, + msecs_to_jiffies(100)); + } + } else { /* card insert */ + cancel_delayed_work(&host->mmc->detect); + mmc_detect_change(host->mmc, msecs_to_jiffies(200)); + } +} + +static irqreturn_t phytium_sdci_irq(int irq, void *dev_id) +{ + struct phytium_sdci_host *host = (struct phytium_sdci_host *) dev_id; + unsigned long flags; + struct mmc_request *mrq; + struct mmc_command *cmd; + u32 events; + + if (!host) + return IRQ_NONE; + + spin_lock_irqsave(&host->lock, flags); + events = readl(host->base + SDCI_NORMAL_ISR); + /* clear interrupts */ + writel(1, host->base + SDCI_NORMAL_ISR); + + mrq = host->mrq; + cmd = host->cmd; + spin_unlock_irqrestore(&host->lock, flags); + + if (events & (SDCI_NORMAL_ISR_CR | SDCI_NORMAL_ISR_CI)) { + mod_timer(&host->hotplug_timer, + jiffies + usecs_to_jiffies(30000)); + goto irq_out; + } + + if (!(events & cmd_ints_mask)) + goto irq_out; + + if (!mrq) { + dev_err(host->dev, "%s: MRQ=NULL; events=%08X\n", + __func__, events); + WARN_ON(1); + goto irq_out; + } + + dev_dbg(host->dev, "%s: events=%08X\n", __func__, events); + + if (cmd) + phytium_sdci_cmd_done(host, events, mrq, cmd); + +irq_out: + return IRQ_HANDLED; +} + +static irqreturn_t phytium_sdci_dma_irq(int irq, void *dev_id) +{ + struct phytium_sdci_host *host = (struct phytium_sdci_host *) dev_id; + unsigned long flags; + struct mmc_request *mrq; + struct mmc_command *cmd; + struct mmc_data *data; + u32 events; + + spin_lock_irqsave(&host->lock, flags); + events = readl(host->base + SDCI_BD_ISR); + writel(1, host->base + SDCI_BD_ISR); + + mrq = host->mrq; + cmd = host->cmd; + data = host->data; + spin_unlock_irqrestore(&host->lock, flags); + + if (!(events & data_ints_mask)) + goto dma_irq_out; + + if (!mrq) { + dev_err(host->dev, + "%s: MRQ=NULL; events=%08X\n", + __func__, events); + goto dma_irq_out; + } + + dev_dbg(host->dev, "%s: events=%08X\n", __func__, events); + + if (data) + phytium_sdci_data_xfer_done(host, events, mrq, data); + +dma_irq_out: + return IRQ_HANDLED; +} + +static irqreturn_t phytium_sdci_err_irq(int irq, void *dev_id) +{ + struct phytium_sdci_host *host = (struct phytium_sdci_host *) dev_id; + unsigned long flags; + struct mmc_request *mrq; + struct mmc_command *cmd; + struct mmc_data *data; + u32 events; + + if (!host) + return IRQ_NONE; + + spin_lock_irqsave(&host->lock, flags); + events = readl(host->base + SDCI_ERROR_ISR); + mrq = host->mrq; + cmd = host->cmd; + data = host->data; + spin_unlock_irqrestore(&host->lock, flags); + + if (!(events&err_ints_mask)) + goto err_irq_out; + + if (!mrq) { + sdr_clr_bits(host->base + SDCI_NORMAL_ISER, SDCI_NORMAL_ISR_EI); + writel(1, host->base + SDCI_ERROR_ISR); + dev_err(host->dev, "%s: MRQ=NULL; events=%08X\n", __func__, events); + goto err_irq_out; + } + sdr_clr_bits(host->base + SDCI_NORMAL_ISER, SDCI_NORMAL_ISR_EI); + if (data) { + dev_err(host->dev, + "[%s][%d]: cmd(%d); %d read blocks, status:%x,flag:%x\n", + __func__, __LINE__, mrq->cmd->opcode, data->blocks, events, data->flags); + data->error = -ETIMEDOUT; + if ((data->flags & MMC_DATA_READ) == MMC_DATA_READ || + (data->flags & MMC_DATA_WRITE) == MMC_DATA_WRITE) + phytium_sdci_data_xfer_done(host, SDCI_BD_ISR_EDTE | SDCI_BD_ISR_TRS_R, + mrq, data); + mrq->cmd->error = -ETIMEDOUT; + mmc_request_done(host->mmc, mrq); + } else if (cmd) { + phytium_sdci_cmd_done(host, SDCI_NORMAL_ISR_TIMEOUT, mrq, cmd); + } + + writel(1, host->base + SDCI_NORMAL_ISR); + writel(1, host->base + SDCI_ERROR_ISR); +err_irq_out: + return IRQ_HANDLED; +} + +static void phytium_sdci_init_hw(struct phytium_sdci_host *host) +{ + u32 val; + + /* Reset */ + phytium_sdci_reset_hw(host); + + val = SDCI_SEN_CREFR_VAL | SDCI_SEN_DEBNCE_VAL; + writel(val, host->base + SDCI_SD_SEN); + + /* Disable and clear all interrupts */ + writel(0, host->base + SDCI_NORMAL_ISER); + writel(0, host->base + SDCI_ERROR_ISER); + writel(0, host->base + SDCI_BD_ISER); + + writel(1, host->base + SDCI_NORMAL_ISR); + writel(1, host->base + SDCI_ERROR_ISR); + writel(1, host->base + SDCI_BD_ISR); + + sdr_set_bits(host->base + SDCI_NORMAL_ISER, + SDCI_SDCI_NORMAL_ISER_ECI|SDCI_SDCI_NORMAL_ISER_ECR); + /* Configure default cmd timeout to 0.1(s)s = val/25M */ + val = SDCI_F_MAX / 10; + writel(val, host->base + SDCI_TIMEOUT_CMD); + writel(SDCI_TIMEOUT_DATA_VALUE, host->base + SDCI_TIMEOUT_DATA); + + val = 0x0F00; + writel(val, host->base + SDCI_CONTROLLER); + + dev_dbg(host->dev, "init hardware done!"); +} + +static void phytium_sdci_deinit_hw(struct phytium_sdci_host *host) +{ + /* Disable and clear all interrupts */ + writel(0, host->base + SDCI_NORMAL_ISER); + writel(0, host->base + SDCI_ERROR_ISER); + writel(0, host->base + SDCI_BD_ISER); + + writel(0, host->base + SDCI_NORMAL_ISR); + writel(0, host->base + SDCI_ERROR_ISR); + writel(0, host->base + SDCI_BD_ISR); +} + +static void phytium_sdci_ops_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct phytium_sdci_host *host = mmc_priv(mmc); + + if (ios->bus_width == MMC_BUS_WIDTH_4) + mmc->caps = mmc->caps & (~MMC_CAP_4_BIT_DATA); + + /* Suspend/Resume will do power off/on */ + switch (ios->power_mode) { + case MMC_POWER_UP: + writel(SDCI_POWER_ON, host->base + SDCI_POWER); + break; + case MMC_POWER_ON: + phytium_sdci_set_clk(host, ios); + break; + case MMC_POWER_OFF: + writel(SDCI_POWER_OFF, host->base + SDCI_POWER); + break; + default: + break; + } +} + +static int phytium_sdci_get_cd(struct mmc_host *mmc) +{ + struct phytium_sdci_host *host = mmc_priv(mmc); + u32 status = readl(host->base + SDCI_STATUS); + + if (((status >> 19) & 0x1) == 0x1) + return 0; + + return 1; +} + +static void phytium_sdci_hw_reset(struct mmc_host *mmc) +{ + struct phytium_sdci_host *host = mmc_priv(mmc); + + sdr_set_bits(host->base + SDCI_SOFTWARE, SDCI_SOFTWARE_SRST); + sdr_clr_bits(host->base + SDCI_SOFTWARE, SDCI_SOFTWARE_SRST); + while (!(readl(host->base + SDCI_STATUS) & SDCI_STATUS_IDIE)) + cpu_relax(); +} + +static struct mmc_host_ops phytium_sdci_ops = { + .request = phytium_sdci_ops_request, + .set_ios = phytium_sdci_ops_set_ios, + .get_cd = phytium_sdci_get_cd, + .card_busy = phytium_sdci_card_busy, + .hw_reset = phytium_sdci_hw_reset, +}; + +static bool phytium_sdci_private_send_cmd(struct phytium_sdci_host *host, + u32 cmd, u32 resp_type, u32 arg) +{ + u32 temp, sd_cmd, sd_arg, sd_status; + unsigned long deadline_time; + + writel(1, host->base + SDCI_NORMAL_ISR); + writel(1, host->base + SDCI_ERROR_ISR); + + sd_cmd = (cmd << 8) | resp_type; + sd_arg = arg; + writel(sd_cmd, host->base + SDCI_COMMAND); + writel(sd_arg, host->base + SDCI_ARGUMENT); + + if (cmd == MMC_STOP_TRANSMISSION) + deadline_time = jiffies + msecs_to_jiffies(1000); + else + deadline_time = jiffies + msecs_to_jiffies(100); + + temp = readl(host->base + SDCI_NORMAL_ISR); + while ((temp & SDCI_NORMAL_ISR_CC) != SDCI_NORMAL_ISR_CC) { + sd_status = readl(host->base + SDCI_STATUS); + if (sd_status & SDCI_STATUS_CDSL) + return false; + + temp = readl(host->base + SDCI_NORMAL_ISR); + if (time_after(jiffies, deadline_time)) + return false; + + if (cmd == MMC_STOP_TRANSMISSION) + mdelay(1); + } + + return true; +} + +static int phytium_sdci_probe(struct platform_device *pdev) +{ + struct mmc_host *mmc; + struct phytium_sdci_host *host; + struct resource *res; + int ret; + const struct acpi_device_id *match; + struct device *dev = &pdev->dev; + + /* Allocate MMC host for this device */ + mmc = mmc_alloc_host(sizeof(struct phytium_sdci_host), &pdev->dev); + if (!mmc) + return -ENOMEM; + + host = mmc_priv(mmc); + ret = mmc_of_parse(mmc); + if (ret) + goto host_free; + + if (dev->of_node) { + host->src_clk = devm_clk_get(&pdev->dev, "phytium_sdc_clk"); + if (IS_ERR(host->src_clk)) { + ret = PTR_ERR(host->src_clk); + goto host_free; + } + + host->clk_rate = clk_get_rate(host->src_clk); + if (device_property_read_bool(dev, "no-dma-coherent")) + dev->dma_coherent = false; + } else if (has_acpi_companion(dev)) { + match = acpi_match_device(dev->driver->acpi_match_table, dev); + if (!match) { + dev_err(dev, "Error ACPI match data is missing\n"); + return -ENODEV; + } + + acpi_dma_configure(dev, DEV_DMA_NOT_SUPPORTED); + + host->clk_rate = 600000000; + } else { + dev_err(&pdev->dev, "No DT found\n"); + return -EINVAL; + } + + dma_set_mask(dev, DMA_BIT_MASK(40)); + dma_set_coherent_mask(dev, DMA_BIT_MASK(40)); + + timer_setup(&host->hotplug_timer, hotplug_timer_func, 0); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + host->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(host->base)) { + ret = PTR_ERR(host->base); + goto host_free; + } + + host->irq = platform_get_irq(pdev, 1); + if (host->irq < 0) { + ret = -EINVAL; + goto host_free; + } + + host->irq_err = platform_get_irq(pdev, 2); + if (host->irq_err < 0) { + ret = -EINVAL; + goto host_free; + } + + host->irq_bd = platform_get_irq(pdev, 0); + if (host->irq_bd < 0) { + ret = -EINVAL; + goto host_free; + } + + host->dev = &pdev->dev; + host->mmc = mmc; + + if ((4 * SDCI_F_MAX) > host->clk_rate) + host->clk_div = 1; + else + host->clk_div = ((host->clk_rate / (2 * SDCI_F_MAX)) - 1); + + /* Set host parameters to mmc */ + mmc->f_min = SDCI_F_MIN; + mmc->f_max = (host->clk_rate / ((host->clk_div + 1) * 2)); + mmc->ops = &phytium_sdci_ops; + mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; + + mmc->caps |= host->caps; + /* MMC core transfer sizes tunable parameters */ + mmc->max_segs = MAX_BD_NUM; + mmc->max_seg_size = 512 * 1024; + mmc->max_blk_size = 512; + mmc->max_req_size = 512 * 1024; + mmc->max_blk_count = mmc->max_req_size / 512; + + host->dma_rx.buf = dma_alloc_coherent(&pdev->dev, + MAX_BD_NUM, + &host->dma_rx.bd_addr, + GFP_KERNEL); + if (!host->dma_rx.buf) { + ret = -ENOMEM; + goto release_mem; + } + + host->cmd_timeout = msecs_to_jiffies(100); + host->data_timeout = msecs_to_jiffies(250); + + INIT_DELAYED_WORK(&host->req_timeout, phytium_sdci_request_timeout); + spin_lock_init(&host->lock); + + platform_set_drvdata(pdev, mmc); + phytium_sdci_init_hw(host); + + ret = devm_request_irq(&pdev->dev, host->irq, phytium_sdci_irq, + IRQF_SHARED, pdev->name, host); + if (ret) + goto release; + + ret = devm_request_irq(&pdev->dev, host->irq_err, phytium_sdci_err_irq, + IRQF_SHARED, pdev->name, host); + if (ret) + goto release; + + ret = devm_request_irq(&pdev->dev, host->irq_bd, phytium_sdci_dma_irq, + IRQF_SHARED, pdev->name, host); + if (ret) + goto release; + + ret = mmc_add_host(mmc); + if (ret) + goto release; + + return 0; + +release: + platform_set_drvdata(pdev, NULL); + phytium_sdci_deinit_hw(host); +release_mem: + if (host->dma_rx.buf) + dma_free_coherent(&pdev->dev, MAX_BD_NUM, + host->dma_rx.buf, + host->dma_rx.bd_addr); +host_free: + mmc_free_host(mmc); + + return ret; +} + +static int phytium_sdci_remove(struct platform_device *pdev) +{ + struct mmc_host *mmc; + struct phytium_sdci_host *host; + + mmc = platform_get_drvdata(pdev); + host = mmc_priv(mmc); + + cancel_delayed_work_sync(&host->req_timeout); + platform_set_drvdata(pdev, NULL); + mmc_remove_host(host->mmc); + phytium_sdci_deinit_hw(host); + + if (host->dma_rx.buf) + dma_free_coherent(&pdev->dev, MAX_BD_NUM, + host->dma_rx.buf, host->dma_rx.bd_addr); + + mmc_free_host(host->mmc); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int phytium_sdci_suspend(struct device *dev) +{ + struct mmc_host *mmc = dev_get_drvdata(dev); + struct phytium_sdci_host *host = mmc_priv(mmc); + + phytium_sdci_deinit_hw(host); + return 0; +} + +static int phytium_sdci_resume(struct device *dev) +{ + struct mmc_host *mmc = dev_get_drvdata(dev); + struct phytium_sdci_host *host = mmc_priv(mmc); + + phytium_sdci_init_hw(host); + mmc->caps = mmc->caps | MMC_CAP_4_BIT_DATA; + + return 0; +} +#endif + +#ifdef CONFIG_PM +static int phytium_sdci_runtime_suspend(struct device *dev) +{ + struct mmc_host *mmc = dev_get_drvdata(dev); + struct phytium_sdci_host *host = mmc_priv(mmc); + + phytium_sdci_deinit_hw(host); + + return 0; +} + +static int phytium_sdci_runtime_resume(struct device *dev) +{ + struct mmc_host *mmc = dev_get_drvdata(dev); + struct phytium_sdci_host *host = mmc_priv(mmc); + + phytium_sdci_init_hw(host); + + return 0; +} + +static const struct dev_pm_ops phytium_sdci_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(phytium_sdci_suspend, + phytium_sdci_resume) + SET_RUNTIME_PM_OPS(phytium_sdci_runtime_suspend, + phytium_sdci_runtime_resume, NULL) +}; +#else +#define phytium_sdci_dev_pm_ops NULL +#endif + +static const struct of_device_id phytium_sdci_of_ids[] = { + { .compatible = "phytium,sdci", }, + { } +}; +MODULE_DEVICE_TABLE(of, phytium_sdci_of_ids); + +#ifdef CONFIG_ACPI +static const struct acpi_device_id phytium_sdci_acpi_ids[] = { + { .id = "PHYT0005" }, + { } +}; + +MODULE_DEVICE_TABLE(acpi, phytium_sdci_acpi_ids); +#else +#define phytium_sdci_acpi_ids NULL +#endif + +static struct platform_driver phytium_sdci_driver = { + .probe = phytium_sdci_probe, + .remove = phytium_sdci_remove, + .driver = { + .name = "sdci-phytium", + .of_match_table = phytium_sdci_of_ids, + .acpi_match_table = phytium_sdci_acpi_ids, + .pm = &phytium_sdci_dev_pm_ops, + }, +}; + +module_platform_driver(phytium_sdci_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Cheng Quan "); +MODULE_AUTHOR("Chen Baozi "); +MODULE_DESCRIPTION("Phytium SD Card Interface driver"); diff --git a/target/linux/phytium/files-5.10/drivers/mmc/host/phytium-sdci.h b/target/linux/phytium/files-5.10/drivers/mmc/host/phytium-sdci.h new file mode 100755 index 000000000..be0e9aa65 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/mmc/host/phytium-sdci.h @@ -0,0 +1,200 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Phytium SD Card Interface driver + * + * Copyright (c) 2019-2023, Phytium Technology Co.,Ltd. + */ + +/*---------------------------------------------------------------------------*/ +/* Common Definition */ +/*---------------------------------------------------------------------------*/ +#define MAX_BD_NUM 0x1000 +#define SD_BLOCK_SIZE 512 + +/*---------------------------------------------------------------------------*/ +/* Register Offset */ +/*---------------------------------------------------------------------------*/ +#define SDCI_CONTROLLER 0x00 /* controller config reg */ +#define SDCI_ARGUMENT 0x04 /* argument reg */ +#define SDCI_COMMAND 0x08 /* command reg */ +#define SDCI_CLOCK_D 0x0C /* clock divide reg */ +#define SDCI_SOFTWARE 0x10 /* controller reset reg */ +#define SDCI_POWER 0X14 /* POWRE CONTROL REG */ +#define SDCI_TIMEOUT_CMD 0x18 /* cmd timeout config reg */ +#define SDCI_TIMEOUT_DATA 0x1C /* data timeout reg */ +#define SDCI_NORMAL_ISER 0x20 /* normal ISR config reg */ +#define SDCI_ERROR_ISER 0x24 /* erroe ISR config reg */ +#define SDCI_BD_ISER 0x28 /* BD ISR config reg */ +#define SDCI_CAPA 0x2C /* BD ISR config reg */ +#define SDCI_SD_DRV 0x30 /* SD card driving phase position reg */ +#define SDCI_SD_SAMP 0x34 /* SD card sampling phase position reg */ +#define SDCI_SD_SEN 0x38 /* SD card detection reg */ +#define SDCI_HDS_AXI 0x3C /* AXI boundary config reg */ +#define SDCI_BD_RX 0x40 /* BD rx addr reg */ +#define SDCI_BD_TX 0x60 /* BD tx addr reg */ +#define SDCI_BLK_CNT 0x80 /* r/w block num reg */ +#define SDCI_NORMAL_ISR 0xC0 /* normal ISR status reg */ +#define SDCI_ERROR_ISR 0xC4 /* error ISR status reg */ +#define SDCI_BD_ISR 0xC8 /* BD ISR status reg */ +#define SDCI_BD_STATUS 0xCC /* BD descriptor status reg */ +#define SDCI_STATUS 0xD0 /* status reg */ +#define SDCI_BLOCK 0xD4 /* block len reg */ +#define SDCI_RESP0 0xE0 /* response reg0 */ +#define SDCI_RESP1 0xE4 /* response reg1 */ +#define SDCI_RESP2 0xE8 /* response reg2 */ +#define SDCI_RESP3 0XEC /* response reg3 */ + +/*---------------------------------------------------------------------------*/ +/* Register Mask */ +/*---------------------------------------------------------------------------*/ +/* SDCI_CONTROLLER mask */ +#define SDCI_CONTROLLER_ECRCWR (0x1 << 0) /* RW */ +#define SDCI_CONTROLLER_ECRCRD (0x1 << 1) /* RW */ +#define SDCI_CONTROLLER_RESEDE (0x1 << 2) /* RW */ +#define SDCI_CONTROLLER_PERMDR (0x3 << 8) /* RW */ +#define SDCI_CONTROLLER_PERMDX (0x3 << 10) /* RW */ + +/* SDCI_SOFTWARE mask */ +#define SDCI_SOFTWARE_SRST (0x1 << 0) /* RW */ +#define SDCI_SOFTWARE_SCRST (0x1 << 1) /* RW */ +#define SDCI_SOFTWARE_BDRST (0x1 << 2) /* RW */ +#define SDCI_SOFTWARE_CFCLF (0x1 << 3) /* RW */ +#define SDCI_SOFTWARE_SDRST (0x1 << 4) /* RW */ + +/* SDCI_NORMAL_ISER mask */ +#define SDCI_SDCI_NORMAL_ISER_ECC_EN (0x1 << 0) /* RW */ +#define SDCI_SDCI_NORMAL_ISER_ECR (0x1 << 1) /* RW */ +#define SDCI_SDCI_NORMAL_ISER_ECI (0x1 << 2) /* RW */ +#define SDCI_SDCI_NORMAL_ISER_EEI_EN (0x1 << 15) /* RW */ + +/* SDCI_NORMAL_ISR mask */ +#define SDCI_NORMAL_ISR_CC (0x1 << 0) /* R */ +#define SDCI_NORMAL_ISR_CR (0x1 << 1) /* R */ +#define SDCI_NORMAL_ISR_CI (0x1 << 2) /* R */ +#define SDCI_NORMAL_ISR_TIMEOUT (0x1 << 3) /* R */ +#define SDCI_NORMAL_ISR_EI (0x1 << 15) /* R */ + +/* SDCI_ERROR_ISER mask */ +#define SDCI_ERROR_ISER_ECTE_EN (0x1 << 0) /* RW */ +#define SDCI_ERROR_ISR_CCRCE_EN (0x1 << 1) /* RW */ +#define SDCI_ERROR_ISR_CIR_EN (0x1 << 3) /* RW */ +#define SDCI_ERROR_ISR_CNR_EN (0x1 << 4) /* RW */ +/* SDCI_ERROR_ISR mask */ +#define SDCI_ERROR_ISR_CTE (0x1 << 0) /* R */ +#define SDCI_ERROR_ISR_CCRCE (0x1 << 1) /* R */ +#define SDCI_ERROR_ISR_CIR (0x1 << 3) /* R */ +#define SDCI_ERROR_ISR_CNR (0x1 << 4) /* R */ + +/* SDCI_BD_ISER mask */ +#define SDCI_BD_ISER_ETRS_EN (0x1 << 8) /* RW */ +#define SDCI_BD_ISER_DATFRAX_EN (0x1 << 7) /* RW */ + +/* SDCI_BD_ISR mask */ +#define SDCI_BD_ISR_TRS_W (0x1 << 0) /* R */ +#define SDCI_BD_ISR_TRS_R (0x1 << 8) /* R */ +#define SDCI_BD_ISR_EDTE (0x1 << 3) /* R */ +#define SDCI_BD_ISR_DAIS (0x1 << 15) /* R */ +#define SDCI_BD_ISR_DATFRAX (0x1 << 7) /* R */ + +/* SDCI_HDS_AXI mask */ +#define SDCI_HDS_AXI_AWDOMAIN (0x1 << 0) /* RW */ +#define SDCI_HDS_AXI_ARDOMAIN (0x1 << 12) /* RW */ +#define SDCI_HDS_AXI_AWCACHE (0x6 << 24) /* RW */ +#define SDCI_HDS_AXI_ARCACHE (0xB << 28) /* RW */ + +/* SDCI_STATUS mask */ +#define SDCI_STATUS_CMD_BUSY (0x0 << 0) /* R */ +#define SDCI_STATUS_CMD_READY (0x1 << 0) /* R */ +#define SDCI_STATUS_IDIE (0x1 << 12) /* R */ +#define SDCI_CARD_BUSY_IN_PRG (0x1 << 20) /* R D0 BUSY:0,IDLE:1 */ + +/* SDCI_STATUS */ +#define SDCI_STATUS_CDSL (0x1 << 19) /* R */ + +/*---------------------------------------------------------------------------*/ +/* Register Value */ +/*---------------------------------------------------------------------------*/ +#define SDCI_SD_DRV_VALUE 0 +#define SDCI_SD_SAMP_VALUE_MAX 50 +#define SDCI_SD_SAMP_VALUE_MIN 0 + +#define SDCI_TIMEOUT_CMD_VALUE 0xFFFFFFFF +#define SDCI_TIMEOUT_DATA_VALUE 0xFFFFFFFF +#define SDCI_POWER_ON 1 +#define SDCI_POWER_OFF 0 + +#define SDCI_CMD_TIMEOUT 10 +#define SDCI_DAT_TIMEOUT 5000 + +#define SDCI_CMD_TYPE_ADTC 0x2 + +#define SDCI_F_MIN 400000 +#define SDCI_F_MAX 25000000 + +#define SDCI_SEN_CREFR_VAL (0x1 << 1) +#define SDCI_SEN_DEBNCE_VAL (0xB << 8) + +#define CARD_CURRENT_STATE (0xF << 9) +#define CARD_PRG_STATE (0x7 << 9) +#define CARD_TRAN_STATE (0x4 << 9) + +#define SDCI_CMD13_OK 1 +#define SDCI_CMD13_FAILED 0 + +#define ERR_TIMEOUT (0x1 << 0) +#define ERR_CARD_ABSENT (0x1 << 1) +#define ERR_CMD_RESPONED (0x1 << 2) + +/*---------------------------------------------------------------------------*/ +/* Structure Type */ +/*---------------------------------------------------------------------------*/ +struct phytium_sdci_dma { + struct scatterlist *sg; + u32 *buf; + dma_addr_t bd_addr; + size_t bytes; +}; + +enum adtc_type { + COMMOM_ADTC = 0, + BLOCK_RW_ADTC, +}; + +struct phytium_sdci_host { + struct device *dev; + struct mmc_host *mmc; + u32 caps; + spinlock_t lock; + + struct mmc_request *mrq; + struct mmc_command *cmd; + struct mmc_data *data; + int error; + + void __iomem *base; + + struct phytium_sdci_dma dma_rx; + struct phytium_sdci_dma dma_tx; + + u32 *sg_virt_addr; + enum adtc_type adtc_type; + + struct timer_list hotplug_timer; + + struct delayed_work req_timeout; + u32 cmd_timeout; + u32 data_timeout; + + int irq; + int irq_err; + int irq_bd; + + struct clk *src_clk; + unsigned long clk_rate; + unsigned long clk_div; + unsigned long real_rate; + + u32 current_rca; + bool is_multi_rw_only_one_blkcnt; +}; + diff --git a/target/linux/phytium/files-5.10/drivers/mtd/nand/raw/phytium_nand.c b/target/linux/phytium/files-5.10/drivers/mtd/nand/raw/phytium_nand.c new file mode 100644 index 000000000..68728e7ed --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/mtd/nand/raw/phytium_nand.c @@ -0,0 +1,2167 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Core driver for Phytium NAND flash controller + * + * Copyright (c) 2020-2023 Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "phytium_nand.h" + +#define NFC_DATA_CMD_DEBUG 0 + +u16 timing_asy_mode0[TIMING_ASY_NUM] = { /* x100 pass, sample: 1 */ + 0x03, 0x03, 0x28, 0x28, 0x03, 0x03, 0x06, 0x06, 0x28, 0x70, 0x30, 0x50}; +u16 timing_asy_mode1[TIMING_ASY_NUM] = { /* x100 pass, sample: 1 */ + 0x03, 0x03, 0x14, 0x14, 0x03, 0x03, 0x06, 0x06, 0x14, 0x70, 0x30, 0x28}; +u16 timing_asy_mode2[TIMING_ASY_NUM] = { /* x100 pass, sample: 7/8 (unlic) */ + 0x03, 0x03, 0x0D, 0x0D, 0x03, 0x03, 0x06, 0x06, 0x0D, 0x70, 0x20, 0x1A}; +u16 timing_asy_mode3[TIMING_ASY_NUM] = { /* x100 pass, sample: 4-7 */ + 0x03, 0x03, 0x0A, 0x0A, 0x03, 0x03, 0x06, 0x06, 0x0A, 0x70, 0x20, 0x14}; +u16 timing_asy_mode4[TIMING_ASY_NUM] = { /* x100 1.8v pass */ + 0x03, 0x03, 0x08, 0x08, 0x03, 0x03, 0x06, 0x06, 0x08, 0x70, 0x15, 0x10}; +u16 timing_asy_mode5[TIMING_ASY_NUM] = { /* x100 1.8v pass */ + 0x03, 0x03, 0x07, 0x07, 0x03, 0x03, 0x06, 0x06, 0x07, 0x20, 0x15, 0x0E}; +u16 timing_syn_mode0[TIMING_SYN_NUM] = { /* x100 1.8v pass */ + 0x20, 0x41, 0x05, 0x20, 0x10, 0x19, 0x62, 0x40, 0x38, 0x20, 0x00, 0x09, + 0x50, 0x20}; +u16 timing_syn_mode1[TIMING_SYN_NUM] = { /* x100 1.8v pass */ + 0x18, 0x32, 0x06, 0x18, 0x0C, 0x10, 0x76, 0x40, 0x2A, 0x1E, 0x00, 0x12, + 0x24, 0x18}; +u16 timing_syn_mode2[TIMING_SYN_NUM] = { /* x100 1.8v pass */ + 0x10, 0x0A, 0x04, 0x10, 0x08, 0x0A, 0x6E, 0x50, 0x1D, 0x10, 0x00, 0x0C, + 0x18, 0x10}; +u16 timing_syn_mode3[TIMING_SYN_NUM] = { /* x100 1.8v pass */ + 0x0C, 0x1A, 0x02, 0x0C, 0x06, 0x08, 0x78, 0x7C, 0x15, 0x0C, 0x00, 0x08, + 0x12, 0x0C}; +u16 timing_syn_mode4[TIMING_SYN_NUM] = { /* x100 1.8v failed */ + 0x08, 0x17, 0x05, 0x08, 0x04, 0x01, 0x73, 0x40, 0x0C, 0x08, 0x00, 0x06, + 0x0C, 0x10}; +u16 timing_tog_ddr_mode0[TIMING_TOG_NUM] = { /* 600M clk */ + 0x14, 0x0a, 0x08, 0x08, 0xc8, 0xc8, 0x08, 0x08, 0x20, 0x0a, 0x14, 0x08}; + +static u32 nfc_ecc_errover; +static u32 nfc_ecc_err; +static u32 nfc_irq_st; +static u32 nfc_irq_en; +static u32 nfc_irq_complete; + +static DECLARE_WAIT_QUEUE_HEAD(wait_done); + +/* + * Internal helper to conditionnally apply a delay (from the above structure, + * most of the time). + */ +static void cond_delay(unsigned int ns) +{ + if (!ns) + return; + + if (ns < 10000) + ndelay(ns); + else + udelay(DIV_ROUND_UP(ns, 1000)); +} + +static inline struct phytium_nfc *to_phytium_nfc(struct nand_controller *ctrl) +{ + return container_of(ctrl, struct phytium_nfc, controller); +} + +static inline struct phytium_nand_chip *to_phytium_nand(struct nand_chip *chip) +{ + return container_of(chip, struct phytium_nand_chip, chip); +} + +static u32 phytium_read(struct phytium_nfc *nfc, u32 reg) +{ + return readl_relaxed(nfc->regs + reg); +} + +static void phytium_write(struct phytium_nfc *nfc, u32 reg, u32 value) +{ + return writel_relaxed(value, nfc->regs + reg); +} + +static inline int phytium_wait_busy(struct phytium_nfc *nfc) +{ + u32 status; + + if (nfc_ecc_errover) { + nfc_ecc_errover = 0; + return 0; + } + + return readl_relaxed_poll_timeout(nfc->regs + NDSR, status, + !(status & NDSR_BUSY), 10, 10000); +} + +static void phytium_nfc_disable_int(struct phytium_nfc *nfc, u32 int_mask) +{ + u32 reg; + + reg = phytium_read(nfc, NDIR_MASK); + phytium_write(nfc, NDIR_MASK, reg | int_mask); +} + +static void phytium_nfc_enable_int(struct phytium_nfc *nfc, u32 int_mask) +{ + u32 reg; + + reg = phytium_read(nfc, NDIR_MASK); + phytium_write(nfc, NDIR_MASK, reg & (~int_mask)); +} + +static void phytium_nfc_clear_int(struct phytium_nfc *nfc, u32 int_mask) +{ + phytium_write(nfc, NDIR_MASK, int_mask); +} + +static int phytium_nfc_cmd_correct(struct phytium_nfc_op *nfc_op) +{ + if (!nfc_op) + return -EINVAL; + + if (nfc_op->cmd_len == 0x01) { + nfc_op->cmd[1] = nfc_op->cmd[0]; + nfc_op->cmd[0] = 0; + } + + return 0; +} + +static int phytium_nfc_addr_correct(struct phytium_nfc_op *nfc_op) +{ + u32 len; + int i, j; + + if (!nfc_op) + return -EINVAL; + + len = nfc_op->addr_len > PHYTIUM_NFC_ADDR_MAX_LEN ? + PHYTIUM_NFC_ADDR_MAX_LEN : nfc_op->addr_len; + + if (len == PHYTIUM_NFC_ADDR_MAX_LEN) + return 0; + + for (i = len-1, j = PHYTIUM_NFC_ADDR_MAX_LEN - 1; i >= 0; i--, j--) { + nfc_op->addr[j] = nfc_op->addr[i]; + nfc_op->addr[i] = 0; + } + + return 0; +} + +static void phytium_nfc_parse_instructions(struct nand_chip *chip, + const struct nand_subop *subop, + struct phytium_nfc_op *nfc_op) +{ + struct nand_op_instr *instr = NULL; + bool first_cmd = true; + u32 op_id; + int i; + + /* Reset the input structure as most of its fields will be OR'ed */ + memset(nfc_op, 0, sizeof(struct phytium_nfc_op)); + + for (op_id = 0; op_id < subop->ninstrs; op_id++) { + unsigned int offset, naddrs; + const u8 *addrs; + int len; + + instr = (struct nand_op_instr *)&subop->instrs[op_id]; + + switch (instr->type) { + case NAND_OP_CMD_INSTR: + if (first_cmd) { + nfc_op->cmd[0] = instr->ctx.cmd.opcode; + } else { + nfc_op->cmd[1] = instr->ctx.cmd.opcode; + nfc_op->cmd_ctrl.nfc_ctrl.dbc = 1; + } + + nfc_op->cle_ale_delay_ns = instr->delay_ns; + first_cmd = false; + nfc_op->cmd_len++; + + break; + + case NAND_OP_ADDR_INSTR: + offset = nand_subop_get_addr_start_off(subop, op_id); + naddrs = nand_subop_get_num_addr_cyc(subop, op_id); + addrs = &instr->ctx.addr.addrs[offset]; + + nfc_op->cmd_ctrl.nfc_ctrl.addr_cyc = naddrs; + + for (i = 0; i < min_t(u32, PHYTIUM_NFC_ADDR_MAX_LEN, naddrs); i++) + nfc_op->addr[i] = addrs[i]; + + nfc_op->cle_ale_delay_ns = instr->delay_ns; + + nfc_op->addr_len = naddrs; + break; + + case NAND_OP_DATA_IN_INSTR: + nfc_op->data_instr = instr; + nfc_op->data_instr_idx = op_id; + nfc_op->cmd_ctrl.nfc_ctrl.dc = 1; + len = nand_subop_get_data_len(subop, op_id); + nfc_op->page_cnt = len; + nfc_op->data_delay_ns = instr->delay_ns; + + break; + + case NAND_OP_DATA_OUT_INSTR: + nfc_op->data_instr = instr; + nfc_op->data_instr_idx = op_id; + nfc_op->cmd_ctrl.nfc_ctrl.dc = 1; + len = nand_subop_get_data_len(subop, op_id); + nfc_op->page_cnt = len; + nfc_op->data_delay_ns = instr->delay_ns; + break; + + case NAND_OP_WAITRDY_INSTR: + nfc_op->rdy_timeout_ms = instr->ctx.waitrdy.timeout_ms; + nfc_op->rdy_delay_ns = instr->delay_ns; + break; + } + } +} + +int phytium_nfc_prepare_cmd(struct nand_chip *chip, + struct phytium_nfc_op *nfc_op, + enum dma_data_direction direction) +{ + struct phytium_nand_chip *phytium_nand = to_phytium_nand(chip); + struct phytium_nfc *nfc = to_phytium_nfc(chip->controller); + int i; + + phytium_nfc_cmd_correct(nfc_op); + phytium_nfc_addr_correct(nfc_op); + + nfc_op->cmd_ctrl.nfc_ctrl.csel = 0x0F ^ (0x01 << phytium_nand->selected_die); + + for (i = 0; i < PHYTIUM_NFC_ADDR_MAX_LEN; i++) + nfc_op->mem_addr_first[i] = (nfc->dma_phy_addr >> (8 * i)) & 0xFF; + + return 0; +} +EXPORT_SYMBOL_GPL(phytium_nfc_prepare_cmd); + +#if NFC_DATA_CMD_DEBUG + +static int phytium_nfc_cmd_dump(struct phytium_nfc *nfc, + struct phytium_nfc_op *nfc_op, u8 *buf) +{ + u8 *p; + u8 str[1024] = {0}; + int i; + + sprintf(str, "Phytium NFC cmd dump:\n"); + sprintf(str, "%s cmd0:%x, cmd1:%x, ctrl:%x, page_cnt:%d\n", + str, nfc_op->cmd[0], nfc_op->cmd[1], nfc_op->cmd_ctrl.ctrl, nfc_op->page_cnt); + + p = &nfc_op->addr[0]; + sprintf(str, "%s addr:%02x %02x %02x %02x %02x\n", + str, p[0], p[1], p[2], p[3], p[4]); + + p = &nfc_op->mem_addr_first[0]; + sprintf(str, "%s mem_addr_first:%02x %02x %02x %02x %02x\n", + str, p[0], p[1], p[2], p[3], p[4]); + + for (i = 0; i < PHYTIUM_NFC_DSP_SIZE; i++) + sprintf(str, "%s %02x", str, buf[i]); + + dev_info(nfc->dev, "%s\n", str); + + return 0; +} + +int phytium_nfc_data_dump(struct phytium_nfc *nfc, u8 *buf, u32 len) +{ + u8 str[1024] = {0}; + int i; + + len = len > 512 ? 512 : len; + + sprintf(str, "Phytium NFC data dump: %d\n", len); + for (i = 0; i < len; i++) { + if (i && (i%128 == 0)) { + dev_info(nfc->dev, "next:\n%s\n", str); + memset(str, 0, 1024); + } + + if (i && (i%16 == 0)) + sprintf(str, "%s\n", str); + sprintf(str, "%s %02x", str, buf[i]); + } + + dev_info(nfc->dev, "%s\n", str); + + return 0; +} +EXPORT_SYMBOL_GPL(phytium_nfc_data_dump); +#else +#define phytium_nfc_cmd_dump(...) +#define phytium_nfc_data_dump(...) +#endif + +int phytium_nfc_send_cmd(struct nand_chip *chip, + struct phytium_nfc_op *nfc_op) +{ + struct phytium_nfc *nfc = to_phytium_nfc(chip->controller); + u32 value = 0; + + memset((u8 *)nfc->dsp_addr, 0, PAGE_SIZE); + memcpy((u8 *)nfc->dsp_addr, (u8 *)nfc_op, PHYTIUM_NFC_DSP_SIZE); + + phytium_nfc_cmd_dump(nfc, nfc_op, (u8 *)nfc->dsp_addr); + + if (phytium_wait_busy(nfc) != 0) { + dev_err(nfc->dev, "NFC was always busy\n"); + dev_err(nfc->dev, "NFC state: %x\n", phytium_read(nfc, NDSR)); + dev_err(nfc->dev, "NFC debug: %x\n", phytium_read(nfc, ND_DEBUG)); + return 0; + } + + spin_lock(&nfc->spinlock); + value = nfc->dsp_phy_addr & 0xFFFFFFFF; + phytium_write(nfc, NDAR0, value); + + /* Don't modify NDAR1_DMA_RLEN & NDAR1_DMA_WLEN */ + value = phytium_read(nfc, NDAR1); + value |= NDAR1_H8((nfc->dsp_phy_addr >> 32) & 0xFF); + phytium_write(nfc, NDAR1, value); + + phytium_nfc_enable_int(nfc, NDIR_CMD_FINISH_MASK); + + value |= NDAR1_DMA_EN; + phytium_write(nfc, NDAR1, value); + spin_unlock(&nfc->spinlock); + + return 0; +} +EXPORT_SYMBOL_GPL(phytium_nfc_send_cmd); + +int phytium_nfc_prepare_cmd2(struct nand_chip *chip, + struct phytium_nfc_op *nfc_op, + enum dma_data_direction direction, + u32 cmd_num) +{ + struct phytium_nand_chip *phytium_nand = to_phytium_nand(chip); + int i; + + for (i = 0; i < cmd_num; i++) { + phytium_nfc_cmd_correct(nfc_op); + phytium_nfc_addr_correct(nfc_op); + nfc_op->cmd_ctrl.nfc_ctrl.csel = 0x0F ^ (0x01 << phytium_nand->selected_die); + nfc_op++; + } + + return 0; +} +EXPORT_SYMBOL_GPL(phytium_nfc_prepare_cmd2); + +int phytium_nfc_send_cmd2(struct nand_chip *chip, + struct phytium_nfc_op *nfc_op, + u32 cmd_num) +{ + struct phytium_nfc *nfc = to_phytium_nfc(chip->controller); + u32 value = 0; + int i; + + memset((u8 *)nfc->dsp_addr, 0, PAGE_SIZE); + + for (i = 0; i < cmd_num; i++) { + memcpy((u8 *)nfc->dsp_addr + i*PHYTIUM_NFC_DSP_SIZE, + (u8 *)nfc_op, PHYTIUM_NFC_DSP_SIZE); + phytium_nfc_cmd_dump(nfc, nfc_op, (u8 *)nfc->dsp_addr + i*PHYTIUM_NFC_DSP_SIZE); + nfc_op++; + } + + if (phytium_wait_busy(nfc) != 0) { + dev_err(nfc->dev, "NFC was always busy\n"); + dev_err(nfc->dev, "NFC state: %x\n", phytium_read(nfc, NDSR)); + dev_err(nfc->dev, "NFC debug: %x\n", phytium_read(nfc, ND_DEBUG)); + return 0; + } + + spin_lock(&nfc->spinlock); + value = nfc->dsp_phy_addr & 0xFFFFFFFF; + phytium_write(nfc, NDAR0, value); + + /* Don't modify NDAR1_DMA_RLEN & NDAR1_DMA_WLEN */ + value = phytium_read(nfc, NDAR1); + value |= NDAR1_H8((nfc->dsp_phy_addr >> 32) & 0xFF); + phytium_write(nfc, NDAR1, value); + + phytium_nfc_enable_int(nfc, NDIR_DMA_FINISH_MASK | NDIR_ECC_ERR_MASK); + + value |= NDAR1_DMA_EN; + phytium_write(nfc, NDAR1, value); + spin_unlock(&nfc->spinlock); + + return 0; +} +EXPORT_SYMBOL_GPL(phytium_nfc_send_cmd2); + +int phytium_nfc_wait_op(struct nand_chip *chip, + u32 timeout_ms) +{ + struct phytium_nfc *nfc = to_phytium_nfc(chip->controller); + int ret; + + /* Timeout is expressed in ms */ + if (!timeout_ms) + timeout_ms = IRQ_TIMEOUT; + else if (timeout_ms > 1000) + timeout_ms = 1000; + else if (timeout_ms < 100) + timeout_ms = 100; + + ret = wait_event_interruptible_timeout(wait_done, nfc_irq_complete, + msecs_to_jiffies(timeout_ms)); + nfc_irq_complete = false; + + if (!ret) { + dev_err(nfc->dev, "Timeout waiting for RB signal\n"); + dev_err(nfc->dev, "NFC state: %x\n", phytium_read(nfc, NDSR)); + dev_err(nfc->dev, "NFC irq state: %x, irq en:%x\n", + phytium_read(nfc, NDIR), phytium_read(nfc, NDIR_MASK)); + dev_err(nfc->dev, "NFC debug: %x\n", phytium_read(nfc, ND_DEBUG)); + + phytium_nfc_clear_int(nfc, NDIR_ALL_INT(nfc->caps->int_mask_bits)); + return -ETIMEDOUT; + } + + return 0; +} +EXPORT_SYMBOL_GPL(phytium_nfc_wait_op); + +static int phytium_nfc_xfer_data_pio(struct phytium_nfc *nfc, + const struct nand_subop *subop, + struct phytium_nfc_op *nfc_op) +{ + const struct nand_op_instr *instr = nfc_op->data_instr; + unsigned int op_id = nfc_op->data_instr_idx; + unsigned int len = nand_subop_get_data_len(subop, op_id); + unsigned int offset = nand_subop_get_data_start_off(subop, op_id); + bool reading = (instr->type == NAND_OP_DATA_IN_INSTR); + + if (reading) { + u8 *in = instr->ctx.data.buf.in + offset; + + memcpy(in, nfc->dma_buf, len); + + nfc->dma_offset = 0; + } else { + const u8 *out = instr->ctx.data.buf.out + offset; + + memcpy(nfc->dma_buf, out, len); + } + + return 0; +} + +static int memcpy_to_reg16(struct phytium_nfc *nfc, u32 reg, u16 *buf, size_t len) +{ + int i; + u32 val = 0; + + if (!nfc || !buf || (len >= 16)) + return -EINVAL; + + for (i = 0; i < len; i++) { + val = (val << 16) + buf[i]; + if (i % 2) { + phytium_write(nfc, reg, val); + val = 0; + reg += 4; + } + } + + return 0; +} + +int phytium_nfc_default_data_interface(struct phytium_nfc *nfc) +{ + int value; + + value = phytium_read(nfc, NDCR0); + value &= (~NDCR0_IN_MODE(3)); + value |= NDCR0_IN_MODE(nfc->inter_mode); + phytium_write(nfc, NDCR0, value); + + value = phytium_read(nfc, NDCR1); + value &= (~NDCR1_SAMPL_PHASE(0xFFFF)); + + switch (nfc->inter_mode) { + case ASYN_SDR: + if (nfc->timing_mode == ASY_MODE4) { + memcpy_to_reg16(nfc, NDTR0, timing_asy_mode4, TIMING_ASY_NUM); + phytium_write(nfc, NDCR1, value | NDCR1_SAMPL_PHASE(4)); + } else if (nfc->timing_mode == ASY_MODE3) { + memcpy_to_reg16(nfc, NDTR0, timing_asy_mode3, TIMING_ASY_NUM); + phytium_write(nfc, NDCR1, value | NDCR1_SAMPL_PHASE(5)); + } else if (nfc->timing_mode == ASY_MODE2) { + memcpy_to_reg16(nfc, NDTR0, timing_asy_mode2, TIMING_ASY_NUM); + phytium_write(nfc, NDCR1, value | NDCR1_SAMPL_PHASE(3)); + } else if (nfc->timing_mode == ASY_MODE1) { + memcpy_to_reg16(nfc, NDTR0, timing_asy_mode1, TIMING_ASY_NUM); + phytium_write(nfc, NDCR1, value | NDCR1_SAMPL_PHASE(2)); + } else { + memcpy_to_reg16(nfc, NDTR0, timing_asy_mode0, TIMING_ASY_NUM); + phytium_write(nfc, NDCR1, value | NDCR1_SAMPL_PHASE(1)); + } + phytium_write(nfc, ND_INTERVAL_TIME, 0x01); + break; + case ONFI_DDR: + if (nfc->timing_mode == SYN_MODE4) { + memcpy_to_reg16(nfc, NDTR6, timing_syn_mode4, TIMING_SYN_NUM); + phytium_write(nfc, NDCR1, value | NDCR1_SAMPL_PHASE(0x0D)); + phytium_write(nfc, ND_INTERVAL_TIME, 0x30); + } else if (nfc->timing_mode == SYN_MODE3) { + memcpy_to_reg16(nfc, NDTR6, timing_syn_mode3, TIMING_SYN_NUM); + phytium_write(nfc, NDCR1, value | NDCR1_SAMPL_PHASE(0x05)); + phytium_write(nfc, ND_INTERVAL_TIME, 0x18); + } else if (nfc->timing_mode == SYN_MODE2) { + memcpy_to_reg16(nfc, NDTR6, timing_syn_mode2, TIMING_SYN_NUM); + phytium_write(nfc, NDCR1, value | NDCR1_SAMPL_PHASE(0x08)); + phytium_write(nfc, ND_INTERVAL_TIME, 0x20); + } else if (nfc->timing_mode == SYN_MODE1) { + memcpy_to_reg16(nfc, NDTR6, timing_syn_mode1, TIMING_SYN_NUM); + phytium_write(nfc, NDCR1, value | NDCR1_SAMPL_PHASE(0x12)); + phytium_write(nfc, ND_INTERVAL_TIME, 0x40); + } else { + memcpy_to_reg16(nfc, NDTR6, timing_syn_mode0, TIMING_SYN_NUM); + phytium_write(nfc, NDCR1, value | NDCR1_SAMPL_PHASE(0x12)); + phytium_write(nfc, ND_INTERVAL_TIME, 0x40); + } + break; + case TOG_ASYN_DDR: + phytium_write(nfc, NDCR1, value | NDCR1_SAMPL_PHASE(8)); + phytium_write(nfc, ND_INTERVAL_TIME, 0xC8); + memcpy_to_reg16(nfc, NDTR13, timing_tog_ddr_mode0, TIMING_TOG_NUM); + break; + } + + return 0; +} +EXPORT_SYMBOL_GPL(phytium_nfc_default_data_interface); + +static int phytium_nfc_naked_waitrdy_exec(struct nand_chip *chip, + const struct nand_subop *subop) +{ + struct phytium_nfc *nfc = to_phytium_nfc(chip->controller); + struct phytium_nfc_op nfc_op; + enum dma_data_direction direction; + int ret = 0; + + phytium_nfc_parse_instructions(chip, subop, &nfc_op); + + dev_info(nfc->dev, "Phytium nand command 0x%02x 0x%02x.\n", + nfc_op.cmd[0], nfc_op.cmd[1]); + + switch (nfc_op.cmd[0]) { + case NAND_CMD_PARAM: + memset(nfc->dma_buf, 0, PAGE_SIZE); + direction = DMA_FROM_DEVICE; + nfc_op.cmd_ctrl.nfc_ctrl.cmd_type = TYPE_READ_PARAM; + nfc_op.cmd_ctrl.nfc_ctrl.auto_rs = 1; + if (nfc->inter_pro == NAND_ONFI) + nfc_op.page_cnt = 3 * sizeof(struct nand_onfi_params); + else if (nfc->inter_pro == NAND_JEDEC) + nfc_op.page_cnt = 3 * sizeof(struct nand_jedec_params); + if (nfc_op.page_cnt) + nfc_op.cmd_ctrl.nfc_ctrl.dc = 1; + nfc->dma_offset = 0; + break; + case NAND_CMD_SET_FEATURES: + direction = DMA_TO_DEVICE; + nfc_op.cmd_ctrl.nfc_ctrl.cmd_type = TYPE_SET_FTR; + nfc_op.cmd_ctrl.nfc_ctrl.auto_rs = 1; + nfc_op.cmd_ctrl.nfc_ctrl.dc = 1; + if (nfc->inter_mode != ASYN_SDR) { + dev_err(nfc->dev, "Not support SET_FEATURES command!\n"); + return 0; + } + break; + case NAND_CMD_GET_FEATURES: + direction = DMA_FROM_DEVICE; + nfc_op.cmd_ctrl.nfc_ctrl.cmd_type = TYPE_GET_FTR; + nfc_op.cmd_ctrl.nfc_ctrl.auto_rs = 1; + nfc_op.cmd_ctrl.nfc_ctrl.dc = 1; + break; + case NAND_CMD_READ0: + if (nfc_op.cmd[1] == NAND_CMD_READSTART) { /* large page */ + direction = DMA_FROM_DEVICE; + nfc_op.cmd_ctrl.nfc_ctrl.cmd_type = TYPE_READ; + nfc_op.cmd_ctrl.nfc_ctrl.auto_rs = 1; + } else if (nfc_op.cmd[1] == NAND_CMD_SEQIN) { /* program page begin */ + nfc_op.cmd[0] = NAND_CMD_SEQIN; + nfc_op.cmd[1] = NAND_CMD_PAGEPROG; + direction = DMA_TO_DEVICE; + nfc_op.cmd_ctrl.nfc_ctrl.cmd_type = TYPE_PAGE_PRO; + nfc_op.cmd_ctrl.nfc_ctrl.auto_rs = 1; + } else { /* small page */ + direction = DMA_FROM_DEVICE; + nfc_op.cmd_ctrl.nfc_ctrl.cmd_type = TYPE_READ; + nfc_op.cmd_ctrl.nfc_ctrl.auto_rs = 1; + } + break; + case NAND_CMD_RNDOUT: /* change read column */ + direction = DMA_FROM_DEVICE; + nfc_op.cmd_ctrl.nfc_ctrl.cmd_type = TYPE_CH_READ_COL; + nfc_op.cmd_ctrl.nfc_ctrl.dc = 1; + break; + case NAND_CMD_READSTART: /* large page */ + direction = DMA_FROM_DEVICE; + nfc_op.cmd_ctrl.nfc_ctrl.cmd_type = TYPE_READ; + nfc_op.cmd_ctrl.nfc_ctrl.auto_rs = 1; + break; + case NAND_CMD_RNDOUTSTART: /* change read column */ + direction = DMA_FROM_DEVICE; + nfc_op.cmd_ctrl.nfc_ctrl.cmd_type = TYPE_CH_READ_COL; + nfc_op.cmd_ctrl.nfc_ctrl.auto_rs = 1; + nfc->dma_offset = nfc_op.addr[1]; + nfc->dma_offset = (nfc->dma_offset << 8) + nfc_op.addr[0]; + break; + case NAND_CMD_SEQIN: /* program begin */ + if (nfc_op.cmd[0] == NAND_CMD_READ0) { + nfc_op.cmd[0] = NAND_CMD_SEQIN; + nfc_op.cmd[1] = NAND_CMD_PAGEPROG; + } + direction = DMA_TO_DEVICE; + nfc_op.cmd_ctrl.nfc_ctrl.cmd_type = TYPE_PAGE_PRO; + nfc_op.cmd_ctrl.nfc_ctrl.auto_rs = 1; + break; + case NAND_CMD_RNDIN: /* change write column */ + direction = DMA_TO_DEVICE; + nfc_op.cmd_ctrl.nfc_ctrl.cmd_type = TYPE_CH_WR_COL; + nfc_op.cmd_ctrl.nfc_ctrl.auto_rs = 1; + break; + case NAND_CMD_PAGEPROG: /* program end */ + nfc_op.cmd[0] = NAND_CMD_RNDIN; + nfc_op.cmd[1] = NAND_CMD_PAGEPROG; + direction = DMA_TO_DEVICE; + nfc_op.cmd_ctrl.nfc_ctrl.cmd_type = TYPE_PAGE_PRO; + nfc_op.cmd_ctrl.nfc_ctrl.auto_rs = 1; + break; + default: + dev_err(nfc->dev, "Not support cmd %d.\n", nfc_op.cmd[1]); + ret = -EINVAL; + goto out; + } + + if ((nfc_op.data_instr) && (direction == DMA_TO_DEVICE)) + phytium_nfc_xfer_data_pio(nfc, subop, &nfc_op); + + phytium_nfc_prepare_cmd(chip, &nfc_op, direction); + phytium_nfc_send_cmd(chip, &nfc_op); + cond_delay(nfc_op.cle_ale_delay_ns); + nfc_op.rdy_timeout_ms = nfc_op.rdy_timeout_ms; + ret = phytium_nfc_wait_op(chip, nfc_op.rdy_timeout_ms); + if (ret) + goto out; + + cond_delay(nfc_op.rdy_delay_ns); + + if ((nfc_op.data_instr) && (direction == DMA_FROM_DEVICE)) + phytium_nfc_xfer_data_pio(nfc, subop, &nfc_op); + +out: + return ret; +} + +static int phytium_nfc_read_id_type_exec(struct nand_chip *chip, + const struct nand_subop *subop) +{ + struct phytium_nfc *nfc = to_phytium_nfc(chip->controller); + struct phytium_nfc_op nfc_op; + enum dma_data_direction direction; + u16 read_len = 0; + int ret; + u8 *buf = nfc->dma_buf; + + memset(nfc->dma_buf, 0, PAGE_SIZE); + direction = DMA_FROM_DEVICE; + + phytium_nfc_parse_instructions(chip, subop, &nfc_op); + read_len = nfc_op.page_cnt; + nfc_op.page_cnt = (read_len & 0x03) ? ((read_len & 0xFFFC) + 4) : read_len; + + nfc_op.cmd_ctrl.nfc_ctrl.cmd_type = TYPE_READ_ID; + nfc_op.cmd_ctrl.nfc_ctrl.auto_rs = 0; + + phytium_nfc_prepare_cmd(chip, &nfc_op, direction); + phytium_nfc_send_cmd(chip, &nfc_op); + cond_delay(nfc_op.cle_ale_delay_ns); + ret = phytium_nfc_wait_op(chip, nfc_op.rdy_timeout_ms); + if (ret) + return ret; + + cond_delay(nfc_op.rdy_delay_ns); + + if (!strncmp(nfc->dma_buf, "ONFI", 4)) { + nfc->inter_pro = NAND_ONFI; + } else if (!strncmp(nfc->dma_buf, "JEDEC", 5)) { + nfc->inter_pro = NAND_JEDEC; + if (buf[5] == 1) + nfc->inter_mode = ASYN_SDR; + else if (buf[5] == 2) + nfc->inter_mode = TOG_ASYN_DDR; + else if (buf[5] == 4) + nfc->inter_mode = ASYN_SDR; + } else { + nfc->inter_pro = NAND_OTHER; + } + + dev_info(nfc->dev, "Nand protocol: %d, interface mode: %d\n", + nfc->inter_pro, nfc->inter_mode); + + phytium_nfc_xfer_data_pio(nfc, subop, &nfc_op); + + return 0; +} + +static int phytium_nfc_read_status_exec(struct nand_chip *chip, + const struct nand_subop *subop) +{ + struct phytium_nfc *nfc = to_phytium_nfc(chip->controller); + struct phytium_nfc_op nfc_op; + enum dma_data_direction direction; + u16 read_len = 0; + u32 timeout, count = 0; + int ret = 0; + + direction = DMA_FROM_DEVICE; + + phytium_nfc_parse_instructions(chip, subop, &nfc_op); + read_len = nfc_op.page_cnt; + nfc_op.page_cnt = (read_len & 0x03) ? ((read_len & 0xFFFC) + 4) : read_len; + nfc_op.cmd_ctrl.nfc_ctrl.cmd_type = TYPE_READ_STATUS; + phytium_nfc_prepare_cmd(chip, &nfc_op, direction); + +read_status_retry: + count++; + phytium_nfc_send_cmd(chip, &nfc_op); + cond_delay(nfc_op.cle_ale_delay_ns); + timeout = nfc_op.rdy_timeout_ms ? nfc_op.rdy_timeout_ms : 10; + ret = phytium_nfc_wait_op(chip, nfc_op.rdy_timeout_ms); + if (ret) + goto out; + + phytium_nfc_xfer_data_pio(nfc, subop, &nfc_op); + + if (0xE0 != *(u8 *)(nfc->dma_buf)) { + dev_info(nfc->dev, "Retry to read status (%x)\n", *(u8 *)(nfc->dma_buf)); + + if (count < 10) + goto read_status_retry; + } + +out: + return ret; +} + +static int phytium_nfc_reset_cmd_type_exec(struct nand_chip *chip, + const struct nand_subop *subop) +{ + struct phytium_nfc_op nfc_op; + enum dma_data_direction direction; + + phytium_nfc_parse_instructions(chip, subop, &nfc_op); + + direction = DMA_NONE; + nfc_op.cmd_ctrl.nfc_ctrl.cmd_type = TYPE_RESET; + nfc_op.cmd_ctrl.nfc_ctrl.auto_rs = 1; + phytium_nfc_prepare_cmd(chip, &nfc_op, direction); + phytium_nfc_send_cmd(chip, &nfc_op); + cond_delay(nfc_op.cle_ale_delay_ns); + + return phytium_nfc_wait_op(chip, nfc_op.rdy_timeout_ms); +} + +static int phytium_nfc_erase_cmd_type_exec(struct nand_chip *chip, + const struct nand_subop *subop) +{ + struct phytium_nfc_op nfc_op; + enum dma_data_direction direction; + + phytium_nfc_parse_instructions(chip, subop, &nfc_op); + direction = DMA_NONE; + nfc_op.cmd_ctrl.nfc_ctrl.cmd_type = TYPE_ERASE; + nfc_op.cmd_ctrl.nfc_ctrl.auto_rs = 1; + phytium_nfc_prepare_cmd(chip, &nfc_op, direction); + phytium_nfc_send_cmd(chip, &nfc_op); + cond_delay(nfc_op.cle_ale_delay_ns); + + return phytium_nfc_wait_op(chip, nfc_op.rdy_timeout_ms); +} + +static int phytium_nfc_data_in_type_exec(struct nand_chip *chip, + const struct nand_subop *subop) +{ + struct phytium_nfc *nfc = to_phytium_nfc(chip->controller); + struct phytium_nfc_op nfc_op; + struct nand_op_instr *instr; + unsigned int op_id; + unsigned int len; + unsigned int offset; + u8 *in = NULL; + + phytium_nfc_parse_instructions(chip, subop, &nfc_op); + if (nfc_op.data_instr->type != NAND_OP_DATA_IN_INSTR) { + dev_err(nfc->dev, "Phytium nfc instrs parser failed!\n"); + return -EINVAL; + } + + instr = nfc_op.data_instr; + op_id = nfc_op.data_instr_idx; + len = nand_subop_get_data_len(subop, op_id); + offset = nand_subop_get_data_start_off(subop, op_id); + in = instr->ctx.data.buf.in + offset; + + phytium_nfc_cmd_dump(nfc, &nfc_op, (u8 *)nfc->dsp_addr); + + memcpy(in, nfc->dma_buf + nfc->dma_offset, len); + nfc->dma_offset += len; + phytium_nfc_data_dump(nfc, in, len); + + return 0; +} + +static const struct nand_op_parser phytium_nfc_op_parser = NAND_OP_PARSER( + /* Naked commands not supported, use a function for each pattern */ + NAND_OP_PARSER_PATTERN( + phytium_nfc_read_id_type_exec, + NAND_OP_PARSER_PAT_CMD_ELEM(false), + NAND_OP_PARSER_PAT_ADDR_ELEM(false, PHYTIUM_NFC_ADDR_MAX_LEN), + NAND_OP_PARSER_PAT_DATA_IN_ELEM(false, 8)), + NAND_OP_PARSER_PATTERN( + phytium_nfc_erase_cmd_type_exec, + NAND_OP_PARSER_PAT_CMD_ELEM(false), + NAND_OP_PARSER_PAT_ADDR_ELEM(false, PHYTIUM_NFC_ADDR_MAX_LEN), + NAND_OP_PARSER_PAT_CMD_ELEM(false), + NAND_OP_PARSER_PAT_WAITRDY_ELEM(false)), + NAND_OP_PARSER_PATTERN( + phytium_nfc_read_status_exec, + NAND_OP_PARSER_PAT_CMD_ELEM(false), + NAND_OP_PARSER_PAT_DATA_IN_ELEM(false, 1)), + NAND_OP_PARSER_PATTERN( + phytium_nfc_reset_cmd_type_exec, + NAND_OP_PARSER_PAT_CMD_ELEM(false), + NAND_OP_PARSER_PAT_WAITRDY_ELEM(false)), + NAND_OP_PARSER_PATTERN( + phytium_nfc_naked_waitrdy_exec, + NAND_OP_PARSER_PAT_CMD_ELEM(false), + NAND_OP_PARSER_PAT_ADDR_ELEM(false, PHYTIUM_NFC_ADDR_MAX_LEN), + NAND_OP_PARSER_PAT_WAITRDY_ELEM(false), + NAND_OP_PARSER_PAT_DATA_IN_ELEM(false, MAX_CHUNK_SIZE)), + NAND_OP_PARSER_PATTERN( + phytium_nfc_naked_waitrdy_exec, + NAND_OP_PARSER_PAT_CMD_ELEM(false), + NAND_OP_PARSER_PAT_ADDR_ELEM(false, PHYTIUM_NFC_ADDR_MAX_LEN), + NAND_OP_PARSER_PAT_WAITRDY_ELEM(false)), + NAND_OP_PARSER_PATTERN( + phytium_nfc_naked_waitrdy_exec, + NAND_OP_PARSER_PAT_CMD_ELEM(false), + NAND_OP_PARSER_PAT_ADDR_ELEM(false, PHYTIUM_NFC_ADDR_MAX_LEN), + NAND_OP_PARSER_PAT_DATA_OUT_ELEM(false, 8), + NAND_OP_PARSER_PAT_WAITRDY_ELEM(false)), + NAND_OP_PARSER_PATTERN( + phytium_nfc_naked_waitrdy_exec, + NAND_OP_PARSER_PAT_CMD_ELEM(false), + NAND_OP_PARSER_PAT_ADDR_ELEM(false, PHYTIUM_NFC_ADDR_MAX_LEN), + NAND_OP_PARSER_PAT_CMD_ELEM(false), + NAND_OP_PARSER_PAT_DATA_IN_ELEM(false, MAX_CHUNK_SIZE)), + NAND_OP_PARSER_PATTERN( + phytium_nfc_data_in_type_exec, + NAND_OP_PARSER_PAT_DATA_IN_ELEM(false, MAX_CHUNK_SIZE)), + ); + +static int phytium_nfc_exec_op(struct nand_chip *chip, + const struct nand_operation *op, + bool check_only) +{ + return nand_op_parser_exec_op(chip, &phytium_nfc_op_parser, + op, check_only); +} + +static int phytium_nfc_reset(struct phytium_nfc *nfc) +{ + u32 value; + + phytium_write(nfc, NDIR_MASK, NDIR_ALL_INT(nfc->caps->int_mask_bits)); + phytium_write(nfc, NDSR, NDIR_ALL_INT(nfc->caps->int_mask_bits)); + + phytium_write(nfc, ND_ERR_CLR, 0x0F); + phytium_write(nfc, NDFIFO_CLR, 1); + + value = phytium_read(nfc, NDCR0); + phytium_write(nfc, NDCR0, value & ~(NDCR0_ECC_EN | NDCR0_SPARE_EN)); + + return 0; +} + +static void phytium_nfc_select_chip(struct nand_chip *chip, int die_nr) +{ + struct phytium_nand_chip *phytium_nand = to_phytium_nand(chip); + struct phytium_nfc *nfc = to_phytium_nfc(chip->controller); + + dev_dbg(nfc->dev, "Phytium nand selected chip %d\n", die_nr); + + if (chip == nfc->selected_chip && die_nr == phytium_nand->selected_die) + return; + + if (die_nr < 0 || die_nr >= phytium_nand->nsels) { + nfc->selected_chip = NULL; + phytium_nand->selected_die = -1; + return; + } + + phytium_nfc_reset(nfc); + + nfc->selected_chip = chip; + phytium_nand->selected_die = die_nr; +} + +static int phytium_nand_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + if (section) + return -ERANGE; + + oobregion->length = chip->ecc.total; + oobregion->offset = mtd->oobsize - oobregion->length; + + return 0; +} + +static int phytium_nand_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + if (section) + return -ERANGE; + + /* + * Bootrom looks in bytes 0 & 5 for bad blocks for the + * 4KB page / 4bit BCH combination. + */ + if (mtd->writesize >= SZ_4K) + oobregion->offset = 6; + else + oobregion->offset = 2; + + oobregion->length = mtd->oobsize - chip->ecc.total - oobregion->offset; + + return 0; +} + +static const struct mtd_ooblayout_ops phytium_nand_ooblayout_ops = { + .ecc = phytium_nand_ooblayout_ecc, + .free = phytium_nand_ooblayout_free, +}; + +static void phytium_nfc_enable_hw_ecc(struct nand_chip *chip) +{ + struct phytium_nfc *nfc = to_phytium_nfc(chip->controller); + u32 ndcr0 = phytium_read(nfc, NDCR0); + + if (!(ndcr0 & NDCR0_ECC_EN)) + phytium_write(nfc, NDCR0, ndcr0 | NDCR0_ECC_EN); +} + +static void phytium_nfc_disable_hw_ecc(struct nand_chip *chip) +{ + struct phytium_nfc *nfc = to_phytium_nfc(chip->controller); + u32 ndcr0 = phytium_read(nfc, NDCR0); + + if (ndcr0 & NDCR0_ECC_EN) + phytium_write(nfc, NDCR0, ndcr0 & ~NDCR0_ECC_EN); +} + + +irqreturn_t phytium_nfc_isr(int irq, void *dev_id) +{ + struct phytium_nfc *nfc = dev_id; + u32 st = phytium_read(nfc, NDIR); + u32 ien = (~phytium_read(nfc, NDIR_MASK)) & NDIR_ALL_INT(nfc->caps->int_mask_bits); + + if (!(st & ien)) + return IRQ_NONE; + + nfc_irq_st = st; + nfc_irq_en = ien; + phytium_nfc_disable_int(nfc, st & NDIR_ALL_INT(nfc->caps->int_mask_bits)); + phytium_write(nfc, 0xFD0, 0); + + if (st & (NDIR_CMD_FINISH | NDIR_DMA_FINISH)) { + if (st & NDIR_ECC_ERR) + nfc_ecc_err = 1; + phytium_write(nfc, NDIR, st); + nfc_irq_complete = 1; + } else if (st & (NDIR_FIFO_TIMEOUT | NDIR_PGFINISH)) { + phytium_write(nfc, NDIR, st); + phytium_nfc_enable_int(nfc, (~st) & (NDIR_DMA_FINISH_MASK | + NDIR_PGFINISH_MASK | + NDIR_FIFO_TIMEOUT_MASK | + NDIR_CMD_FINISH_MASK)); + nfc_irq_complete = 0; + } else if (st & NDIR_ECC_ERR) { + phytium_write(nfc, ND_ERR_CLR, 0x08); + phytium_write(nfc, NDIR, st); + phytium_write(nfc, NDFIFO_CLR, 0x01); + nfc_irq_complete = 1; + nfc_ecc_errover = 1; + } else { + phytium_write(nfc, NDIR, st); + nfc_irq_complete = 1; + } + + wake_up(&wait_done); + + return IRQ_HANDLED; +} +EXPORT_SYMBOL(phytium_nfc_isr); + +static int phytium_nfc_hw_ecc_correct(struct nand_chip *chip, + char *buf, int len) +{ + struct phytium_nfc *nfc = to_phytium_nfc(chip->controller); + u32 i, j, value, tmp; + u32 ecc_err_reg_nums; + int stat = 0; + + if (!buf) + return -EINVAL; + + if (nfc->caps->hw_ver == 1) + ecc_err_reg_nums = 2; + else + ecc_err_reg_nums = 4; + + for (i = 0; i < chip->ecc.steps; i++) { + for (j = 0; j < ecc_err_reg_nums; j++) { + value = phytium_read(nfc, 0xB8 + 4 * (ecc_err_reg_nums * i + j)); + dev_info(nfc->dev, "ECC_FLAG: offset:%x value:0x%08x\n", + 0xB8 + 4 * (2 * i + j), value); + + tmp = value & 0xFFFF; + if (tmp && (tmp <= 4096)) { + tmp--; + stat++; + buf[chip->ecc.size * i + (tmp >> 3)] ^= (0x01 << tmp % 8); + dev_info(nfc->dev, "ECC_CORRECT %x %02x\n", + chip->ecc.size * i + (tmp >> 3), + buf[chip->ecc.size * i + (tmp >> 3)]); + dev_info(nfc->dev, "ECC_CORRECT xor %x %02x\n", + 0x01 << (tmp % 8), buf[chip->ecc.size * i + (tmp >> 3)]); + } else if (tmp > 4096) { + dev_info(nfc->dev, "ECC_CORRECT offset > 4096!\n"); + } + + tmp = (value >> 16) & 0xFFFF; + if (tmp && (tmp <= 4096)) { + tmp--; + stat++; + buf[chip->ecc.size * i + (tmp >> 3)] ^= (0x01 << tmp % 8); + dev_info(nfc->dev, "ECC_CORRECT %x %02x\n", + chip->ecc.size * i + (tmp >> 3), + buf[chip->ecc.size * i + (tmp >> 3)]); + dev_info(nfc->dev, "ECC_CORRECT xor %x %02x\n", + chip->ecc.size * i + (tmp >> 3), + buf[chip->ecc.size * i + (tmp >> 3)]); + } else if (tmp > 4096) { + dev_info(nfc->dev, "ECC_CORRECT offset > 4096!\n"); + } + } + } + + return stat; +} + +static int phytium_nand_page_read(struct mtd_info *mtd, struct nand_chip *chip, + u8 *buf, u8 *oob_buf, int oob_len, int page, + bool read) +{ + struct phytium_nfc *nfc = to_phytium_nfc(chip->controller); + struct phytium_nand_chip *phytium_nand = NULL; + const struct nand_sdr_timings *sdr = NULL; + struct phytium_nfc_op nfc_op; + enum dma_data_direction direction; + int ret = 0; + + memset(&nfc_op, 0, sizeof(nfc_op)); + phytium_nand = to_phytium_nand(chip); + sdr = nand_get_sdr_timings(nand_get_interface_config(chip)); + + memset(nfc->dma_buf, 0x0, mtd->writesize + mtd->oobsize); + direction = DMA_FROM_DEVICE; + nfc_op.cmd_ctrl.nfc_ctrl.cmd_type = TYPE_READ; + nfc_op.cmd[0] = NAND_CMD_READ0; + nfc_op.cmd[1] = NAND_CMD_READSTART; + nfc_op.cmd_len = 2; + nfc_op.cle_ale_delay_ns = PSEC_TO_NSEC(sdr->tWB_max); + nfc_op.rdy_timeout_ms = PSEC_TO_MSEC(sdr->tR_max); + nfc_op.rdy_delay_ns = PSEC_TO_NSEC(sdr->tRR_min); + + nfc_op.cmd_ctrl.nfc_ctrl.dbc = 1; + nfc_op.addr[2] = page; + nfc_op.addr[3] = page >> 8; + nfc_op.addr[4] = page >> 16; + nfc_op.addr_len = 5; + nfc_op.cmd_ctrl.nfc_ctrl.addr_cyc = 0x05; + nfc_op.cmd_ctrl.nfc_ctrl.auto_rs = 1; + + nfc_op.page_cnt = mtd->writesize; + nfc_op.cmd_ctrl.nfc_ctrl.dc = 1; + nfc_op.cmd_ctrl.nfc_ctrl.ecc_en = 0; + + /* For data read/program */ + phytium_nfc_prepare_cmd(chip, &nfc_op, direction); + phytium_nfc_send_cmd(chip, &nfc_op); + cond_delay(nfc_op.cle_ale_delay_ns); + + ret = phytium_nfc_wait_op(chip, nfc_op.rdy_timeout_ms); + if (ret) + return ret; + + if ((direction == DMA_FROM_DEVICE) && buf) + memcpy(buf, nfc->dma_buf, mtd->writesize); + + return ret; +} + +static int phytium_nand_oob_read(struct mtd_info *mtd, struct nand_chip *chip, + u8 *buf, u8 *oob_buf, int oob_len, int page, + bool read) +{ + struct phytium_nfc *nfc = to_phytium_nfc(chip->controller); + struct phytium_nand_chip *phytium_nand = NULL; + const struct nand_sdr_timings *sdr = NULL; + struct phytium_nfc_op nfc_op; + enum dma_data_direction direction; + int ret = 0; + + memset(&nfc_op, 0, sizeof(nfc_op)); + phytium_nand = to_phytium_nand(chip); + sdr = nand_get_sdr_timings(nand_get_interface_config(chip)); + + memset(nfc->dma_buf, 0x00, mtd->writesize + mtd->oobsize); + direction = DMA_FROM_DEVICE; + nfc_op.cmd_ctrl.nfc_ctrl.cmd_type = TYPE_READ; + nfc_op.cmd[0] = NAND_CMD_READ0; + nfc_op.cmd[1] = NAND_CMD_READSTART; + nfc_op.cmd_len = 2; + nfc_op.cle_ale_delay_ns = PSEC_TO_NSEC(sdr->tWB_max); + nfc_op.rdy_timeout_ms = PSEC_TO_MSEC(sdr->tR_max); + nfc_op.rdy_delay_ns = PSEC_TO_NSEC(sdr->tRR_min); + + nfc_op.cmd_ctrl.nfc_ctrl.dbc = 1; + nfc_op.addr[2] = page; + nfc_op.addr[3] = page >> 8; + nfc_op.addr[4] = page >> 16; + nfc_op.addr_len = 5; + nfc_op.cmd_ctrl.nfc_ctrl.addr_cyc = 0x05; + nfc_op.cmd_ctrl.nfc_ctrl.auto_rs = 1; + + nfc_op.page_cnt = oob_len; + nfc_op.cmd_ctrl.nfc_ctrl.dc = 1; + nfc_op.cmd_ctrl.nfc_ctrl.ecc_en = 0; + nfc_op.addr[0] = mtd->writesize & 0xFF; + nfc_op.addr[1] = (mtd->writesize >> 8) & 0xFF; + + /* For data read/program */ + phytium_nfc_prepare_cmd(chip, &nfc_op, direction); + phytium_nfc_send_cmd(chip, &nfc_op); + cond_delay(nfc_op.cle_ale_delay_ns); + + ret = phytium_nfc_wait_op(chip, nfc_op.rdy_timeout_ms); + if (ret) + return ret; + + cond_delay(nfc_op.rdy_delay_ns); + + if (direction == DMA_FROM_DEVICE) + memcpy(oob_buf, nfc->dma_buf, oob_len); + + return ret; +} + +static int phytium_nand_get_ecc_total(struct mtd_info *mtd, + struct nand_ecc_ctrl *ecc) +{ + int ecc_total = 0; + + switch (mtd->writesize) { + case 0x200: + if (ecc->strength == 8) + ecc_total = 0x0D; + else if (ecc->strength == 4) + ecc_total = 7; + else if (ecc->strength == 2) + ecc_total = 4; + else + ecc_total = 0; + break; + case 0x800: + if (ecc->strength == 8) + ecc_total = 0x34; + else if (ecc->strength == 4) + ecc_total = 0x1a; + else if (ecc->strength == 2) + ecc_total = 0xd; + else + ecc_total = 0; + break; + case 0x1000: + if (ecc->strength == 8) + ecc_total = 0x68; + else if (ecc->strength == 4) + ecc_total = 0x34; + else if (ecc->strength == 2) + ecc_total = 0x1a; + else + ecc_total = 0; + break; + case 0x2000: + if (ecc->strength == 8) + ecc_total = 0xD0; + else if (ecc->strength == 4) + ecc_total = 0x68; + else if (ecc->strength == 2) + ecc_total = 0x34; + else + ecc_total = 0; + break; + case 0x4000: + if (ecc->strength == 8) + ecc_total = 0x1A0; + if (ecc->strength == 4) + ecc_total = 0xD0; + else if (ecc->strength == 2) + ecc_total = 0x68; + else + ecc_total = 0; + break; + default: + ecc_total = 0; + break; + } + + return ecc_total; +} + +static int phytium_nand_page_read_hwecc(struct mtd_info *mtd, struct nand_chip *chip, + u8 *buf, u8 *oob_buf, int oob_len, int page, + bool read) +{ + struct phytium_nfc *nfc = to_phytium_nfc(chip->controller); + struct phytium_nand_chip *phytium_nand = NULL; + const struct nand_sdr_timings *sdr = NULL; + struct phytium_nfc_op *nfc_op = NULL; + enum dma_data_direction direction; + u32 ecc_offset; + int max_bitflips = 0; + u32 nfc_state = 0; + int ret = 0; + int i; + + phytium_nand = to_phytium_nand(chip); + sdr = nand_get_sdr_timings(nand_get_interface_config(chip)); + + ecc_offset = phytium_nand->ecc.offset; + memset(nfc->dma_buf, 0x00, mtd->writesize + mtd->oobsize); + nfc_op = kzalloc(2 * sizeof(struct phytium_nfc_op), GFP_KERNEL); + if (!nfc_op) { + dev_err(nfc->dev, "Can't malloc space for phytium_nfc_op\n"); + return 0; + } + + nfc_op->cle_ale_delay_ns = PSEC_TO_NSEC(sdr->tWB_max); + nfc_op->rdy_timeout_ms = PSEC_TO_MSEC(sdr->tR_max); + nfc_op->rdy_delay_ns = PSEC_TO_NSEC(sdr->tRR_min); + + direction = DMA_FROM_DEVICE; + nfc_op->cmd_ctrl.nfc_ctrl.cmd_type = TYPE_READ; + nfc_op->cmd[0] = NAND_CMD_READ0; + nfc_op->cmd[1] = NAND_CMD_READSTART; + nfc_op->cmd_len = 2; + nfc_op->addr_len = 5; + nfc_op->cmd_ctrl.nfc_ctrl.dbc = 1; + nfc_op->addr[2] = page; + nfc_op->addr[3] = page >> 8; + nfc_op->addr[4] = page >> 16; + nfc_op->cmd_ctrl.nfc_ctrl.addr_cyc = 0x05; + nfc_op->cmd_ctrl.nfc_ctrl.dc = 1; + nfc_op->cmd_ctrl.nfc_ctrl.auto_rs = 1; + nfc_op->page_cnt = mtd->writesize; + nfc_op->cmd_ctrl.nfc_ctrl.nc = 1; + for (i = 0; i < PHYTIUM_NFC_ADDR_MAX_LEN; i++) + nfc_op->mem_addr_first[i] = (nfc->dma_phy_addr >> (8 * i)) & 0xFF; + + nfc_op++; + memcpy(nfc_op, nfc_op - 1, sizeof(struct phytium_nfc_op)); + nfc_op->cmd_ctrl.nfc_ctrl.cmd_type = TYPE_CH_READ_COL; + nfc_op->cmd[0] = NAND_CMD_RNDOUT; + nfc_op->cmd[1] = NAND_CMD_RNDOUTSTART; + memset(&nfc_op->addr, 0, PHYTIUM_NFC_ADDR_MAX_LEN); + nfc_op->addr_len = 2; + nfc_op->addr[0] = mtd->writesize + phytium_nand->ecc.offset; + nfc_op->addr[1] = (mtd->writesize + phytium_nand->ecc.offset) >> 8; + nfc_op->cmd_ctrl.nfc_ctrl.addr_cyc = 0x02; + nfc_op->page_cnt = phytium_nand_get_ecc_total(mtd, &chip->ecc); + nfc_op->cmd_ctrl.nfc_ctrl.nc = 0; + nfc_op->cmd_ctrl.nfc_ctrl.auto_rs = 0; + nfc_op->cmd_ctrl.nfc_ctrl.ecc_en = 1; + for (i = 0; i < PHYTIUM_NFC_ADDR_MAX_LEN; i++) + nfc_op->mem_addr_first[i] = + ((nfc->dma_phy_addr + mtd->writesize) >> (8 * i)) & 0xFF; + + nfc_op--; + phytium_nfc_prepare_cmd2(chip, nfc_op, direction, 2); + phytium_nfc_send_cmd2(chip, nfc_op, 2); + cond_delay(nfc_op->cle_ale_delay_ns); + + ret = phytium_nfc_wait_op(chip, nfc_op->rdy_timeout_ms); + if (ret){ + kfree(nfc_op); + return ret; + } + + cond_delay(nfc_op->rdy_delay_ns*1000); + + if ((direction == DMA_FROM_DEVICE) && buf) { + nfc_state = phytium_read(nfc, NDSR); + if ((nfc_state & NDSR_ECC_ERROVER) || (nfc_ecc_errover == 1)) { + for (i = 0; i < mtd->writesize/16; i++) { + if (0xFF != *(u8 *)(nfc->dma_buf + i)) { + dev_info(nfc->dev, "NFC: NDSR_ECC_ERROVER %x\n", page); + mtd->ecc_stats.failed++; + mtd->ecc_stats.corrected += max_bitflips; + break; + } + } + } else if (nfc_state & NDSR_ECC_ERR) { + max_bitflips = phytium_nfc_hw_ecc_correct(chip, + nfc->dma_buf, mtd->writesize); + mtd->ecc_stats.corrected += max_bitflips; + dev_info(nfc->dev, "NFC: NDSR_ECC_ERR page:%x, bit:%d\n", + page, max_bitflips); + } + + memcpy(buf, nfc->dma_buf, mtd->writesize); + } + + kfree(nfc_op); + return max_bitflips; +} + +static int phytium_nand_page_write(struct mtd_info *mtd, struct nand_chip *chip, + const u8 *buf, u8 *oob_buf, int oob_len, int page, + bool read) +{ + struct phytium_nfc *nfc = to_phytium_nfc(chip->controller); + struct phytium_nand_chip *phytium_nand = NULL; + const struct nand_sdr_timings *sdr = NULL; + struct phytium_nfc_op nfc_op; + enum dma_data_direction direction; + int ret = 0; + + memset(&nfc_op, 0, sizeof(nfc_op)); + phytium_nand = to_phytium_nand(chip); + sdr = nand_get_sdr_timings(nand_get_interface_config(chip)); + + memcpy(nfc->dma_buf, buf, mtd->writesize); + direction = DMA_TO_DEVICE; + nfc_op.cmd_ctrl.nfc_ctrl.cmd_type = TYPE_PAGE_PRO; + nfc_op.cmd[0] = NAND_CMD_SEQIN; + nfc_op.cmd[1] = NAND_CMD_PAGEPROG; + nfc_op.cmd_len = 2; + nfc_op.addr_len = 5; + nfc_op.cle_ale_delay_ns = PSEC_TO_NSEC(sdr->tWB_max); + nfc_op.rdy_timeout_ms = PSEC_TO_MSEC(sdr->tPROG_max); + nfc_op.rdy_delay_ns = 0; + + nfc_op.cmd_ctrl.nfc_ctrl.dbc = 1; + nfc_op.addr[2] = page; + nfc_op.addr[3] = page >> 8; + nfc_op.addr[4] = page >> 16; + nfc_op.cmd_ctrl.nfc_ctrl.addr_cyc = 0x05; + nfc_op.cmd_ctrl.nfc_ctrl.dc = 1; + nfc_op.cmd_ctrl.nfc_ctrl.auto_rs = 1; + nfc_op.page_cnt = mtd->writesize; + + /* For data read/program */ + phytium_nfc_prepare_cmd(chip, &nfc_op, direction); + phytium_nfc_send_cmd(chip, &nfc_op); + cond_delay(nfc_op.cle_ale_delay_ns); + + ret = phytium_nfc_wait_op(chip, nfc_op.rdy_timeout_ms); + if (ret) + goto out; + + cond_delay(nfc_op.rdy_delay_ns); +out: + return ret; +} + +static int phytium_nand_oob_write(struct mtd_info *mtd, struct nand_chip *chip, + u8 *buf, u8 *oob_buf, int oob_len, int page, + bool read) +{ + struct phytium_nfc *nfc = to_phytium_nfc(chip->controller); + struct phytium_nand_chip *phytium_nand = NULL; + const struct nand_sdr_timings *sdr = NULL; + struct phytium_nfc_op nfc_op; + enum dma_data_direction direction; + int ret = 0; + + memset(&nfc_op, 0, sizeof(nfc_op)); + phytium_nand = to_phytium_nand(chip); + sdr = nand_get_sdr_timings(nand_get_interface_config(chip)); + + direction = DMA_TO_DEVICE; + nfc_op.cmd_ctrl.nfc_ctrl.cmd_type = TYPE_PAGE_PRO; + nfc_op.cmd[0] = NAND_CMD_SEQIN; + nfc_op.cmd[1] = NAND_CMD_PAGEPROG; + nfc_op.cmd_len = 2; + nfc_op.addr_len = 5; + nfc_op.cle_ale_delay_ns = PSEC_TO_NSEC(sdr->tWB_max); + nfc_op.rdy_timeout_ms = PSEC_TO_MSEC(sdr->tPROG_max); + nfc_op.rdy_delay_ns = 0; + + nfc_op.cmd_ctrl.nfc_ctrl.dbc = 1; + nfc_op.addr[2] = page; + nfc_op.addr[3] = page >> 8; + nfc_op.addr[4] = page >> 16; + nfc_op.cmd_ctrl.nfc_ctrl.addr_cyc = 0x05; + nfc_op.cmd_ctrl.nfc_ctrl.dc = 1; + nfc_op.cmd_ctrl.nfc_ctrl.auto_rs = 1; + + nfc_op.page_cnt = oob_len; + nfc_op.cmd_ctrl.nfc_ctrl.ecc_en = 0; + nfc_op.addr[0] = mtd->writesize & 0xFF; + nfc_op.addr[1] = (mtd->writesize >> 8) & 0xFF; + nfc_op.cmd_ctrl.nfc_ctrl.ecc_en = 0; + memcpy(nfc->dma_buf, oob_buf, mtd->oobsize); + + /* For data read/program */ + phytium_nfc_prepare_cmd(chip, &nfc_op, direction); + phytium_nfc_send_cmd(chip, &nfc_op); + cond_delay(nfc_op.cle_ale_delay_ns); + + ret = phytium_nfc_wait_op(chip, nfc_op.rdy_timeout_ms); + if (ret) + goto out; + + cond_delay(nfc_op.rdy_delay_ns); +out: + return ret; +} + +static int phytium_nand_page_write_hwecc(struct mtd_info *mtd, struct nand_chip *chip, + const u8 *buf, u8 *oob_buf, int oob_len, int page, + bool read) +{ + struct phytium_nfc *nfc = to_phytium_nfc(chip->controller); + struct phytium_nand_chip *phytium_nand = NULL; + const struct nand_sdr_timings *sdr = NULL; + struct phytium_nfc_op *nfc_op; + enum dma_data_direction direction; + u32 ecc_offset; + int ret = 0; + int i; + + phytium_nand = to_phytium_nand(chip); + sdr = nand_get_sdr_timings(nand_get_interface_config(chip)); + ecc_offset = phytium_nand->ecc.offset; + + nfc_op = kzalloc(2 * sizeof(struct phytium_nfc_op), GFP_KERNEL); + if (!nfc_op) { + dev_err(nfc->dev, "Can't malloc space for phytium_nfc_op\n"); + return 0; + } + + nfc_op->cle_ale_delay_ns = PSEC_TO_NSEC(sdr->tWB_max); + nfc_op->rdy_timeout_ms = PSEC_TO_MSEC(sdr->tR_max); + nfc_op->rdy_delay_ns = PSEC_TO_NSEC(sdr->tRR_min); + + direction = DMA_TO_DEVICE; + nfc_op->cmd_ctrl.nfc_ctrl.cmd_type = TYPE_CH_ROW_ADDR; + nfc_op->cmd[0] = NAND_CMD_SEQIN; + nfc_op->cmd_len = 1; + nfc_op->addr_len = 5; + nfc_op->cmd_ctrl.nfc_ctrl.dbc = 0; + nfc_op->addr[2] = page; + nfc_op->addr[3] = page >> 8; + nfc_op->addr[4] = page >> 16; + nfc_op->cmd_ctrl.nfc_ctrl.addr_cyc = 0x05; + nfc_op->cmd_ctrl.nfc_ctrl.auto_rs = 0; + nfc_op->cmd_ctrl.nfc_ctrl.nc = 1; + for (i = 0; i < PHYTIUM_NFC_ADDR_MAX_LEN; i++) + nfc_op->mem_addr_first[i] = (nfc->dma_phy_addr >> (8 * i)) & 0xFF; + + /* The first dsp must have data to transfer */ + memcpy(nfc->dma_buf, buf, mtd->writesize); + nfc_op->page_cnt = mtd->writesize; + nfc_op->cmd_ctrl.nfc_ctrl.dc = 1; + + nfc_op++; + memcpy(nfc_op, nfc_op - 1, sizeof(struct phytium_nfc_op)); + nfc_op->cmd_ctrl.nfc_ctrl.cmd_type = TYPE_PAGE_PRO; + nfc_op->cmd_ctrl.nfc_ctrl.dbc = 1; + nfc_op->cmd_ctrl.nfc_ctrl.auto_rs = 1; + nfc_op->cmd[0] = NAND_CMD_RNDIN; + nfc_op->cmd[1] = NAND_CMD_PAGEPROG; + memset(&nfc_op->addr, 0, PHYTIUM_NFC_ADDR_MAX_LEN); + nfc_op->addr_len = 2; + nfc_op->cmd_len = 2; + nfc_op->addr[0] = mtd->writesize + ecc_offset; + nfc_op->addr[1] = (mtd->writesize + ecc_offset) >> 8; + nfc_op->cmd_ctrl.nfc_ctrl.addr_cyc = 0x02; + nfc_op->page_cnt = phytium_nand_get_ecc_total(mtd, &chip->ecc); + nfc_op->cmd_ctrl.nfc_ctrl.nc = 0; + nfc_op->cmd_ctrl.nfc_ctrl.dc = 1; + nfc_op->cmd_ctrl.nfc_ctrl.ecc_en = 1; + for (i = 0; i < PHYTIUM_NFC_ADDR_MAX_LEN; i++) + nfc_op->mem_addr_first[i] = + ((nfc->dma_phy_addr + mtd->writesize + ecc_offset) >> (8 * i)) & 0xFF; + + /* when enable ECC, must offer ecc_offset of oob, but no oobdata */ + nfc_op--; + phytium_nfc_prepare_cmd2(chip, nfc_op, direction, 2); + phytium_nfc_send_cmd2(chip, nfc_op, 2); + cond_delay(nfc_op->cle_ale_delay_ns); + + ret = phytium_nfc_wait_op(chip, nfc_op->rdy_timeout_ms); + if (ret) + goto out; + + cond_delay(nfc_op->rdy_delay_ns*1000); +out: + kfree(nfc_op); + return ret; +} + +static int phytium_nfc_hw_ecc_bch_read_page_raw(struct nand_chip *chip, + u8 *buf, int oob_required, int page) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + u32 oob_len = oob_required ? mtd->oobsize : 0; + int ret; + + ret = phytium_nand_page_read(mtd, chip, buf, NULL, 0, page, true); + if (oob_required) + ret = phytium_nand_oob_read(mtd, chip, NULL, chip->oob_poi, + oob_len, page, true); + + phytium_nfc_data_dump(to_phytium_nfc(chip->controller), buf, mtd->writesize); + + return ret; +} + +static int phytium_nfc_hw_ecc_bch_read_oob_raw(struct nand_chip *chip, int page) +{ + int ret; + struct mtd_info *mtd = nand_to_mtd(chip); + + /* Invalidate page cache */ + chip->pagecache.page = -1; + memset(chip->oob_poi, 0xFF, mtd->oobsize); + + ret = phytium_nand_oob_read(mtd, chip, NULL, chip->oob_poi, + mtd->oobsize, page, true); + + phytium_nfc_data_dump(to_phytium_nfc(chip->controller), chip->oob_poi, mtd->oobsize); + + return ret; +} + +static int phytium_nfc_hw_ecc_bch_read_page(struct nand_chip *chip, + u8 *buf, int oob_required, int page) +{ + int ret; + struct mtd_info *mtd = nand_to_mtd(chip); + u32 oob_len = oob_required ? mtd->oobsize : 0; + struct phytium_nand_chip *phytium_nand = NULL; + + phytium_nand = to_phytium_nand(chip); + + phytium_nfc_enable_hw_ecc(chip); + cond_delay(20*1000); + + ret = phytium_nand_page_read_hwecc(mtd, chip, buf, NULL, + 0, page, true); + + phytium_nfc_disable_hw_ecc(chip); + + if (oob_required) { + oob_len = mtd->oobsize; + ret = phytium_nand_oob_read(mtd, chip, NULL, chip->oob_poi, + oob_len, page, true); + } + + phytium_nfc_data_dump(to_phytium_nfc(chip->controller), buf, mtd->writesize); + + return ret; +} + +static int phytium_nfc_hw_ecc_bch_read_oob(struct nand_chip *chip, + int page) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + u32 oob_len = mtd->oobsize; + int ret; + + ret = phytium_nand_oob_read(mtd, chip, NULL, chip->oob_poi, + oob_len, page, true); + + phytium_nfc_data_dump(to_phytium_nfc(chip->controller), chip->oob_poi, oob_len); + + return ret; +} + +static int phytium_nfc_hw_ecc_bch_write_page_raw(struct nand_chip *chip, + const u8 *buf, int oob_required, int page) +{ + void *oob_buf = oob_required ? chip->oob_poi : NULL; + struct mtd_info *mtd = nand_to_mtd(chip); + + if (oob_required) + phytium_nand_oob_write(mtd, chip, NULL, oob_buf, + mtd->oobsize, page, false); + + return phytium_nand_page_write(mtd, chip, buf, NULL, + 0, page, false); +} + +static int phytium_nfc_hw_ecc_bch_write_page(struct nand_chip *chip, + const u8 *buf, int oob_required, int page) +{ + int ret; + void *oob_buf = oob_required ? chip->oob_poi : NULL; + u32 oob_len; + struct mtd_info *mtd = nand_to_mtd(chip); + + if (oob_required) { + oob_len = mtd->oobsize; + phytium_nand_oob_write(mtd, chip, NULL, oob_buf, + oob_len, page, false); + } + + phytium_nfc_enable_hw_ecc(chip); + + cond_delay(20*1000); + + ret = phytium_nand_page_write_hwecc(mtd, chip, buf, NULL, + 0, page, false); + + phytium_nfc_disable_hw_ecc(chip); + + return ret; +} + +static int phytium_nfc_hw_ecc_bch_write_oob_raw(struct nand_chip *chip, int page) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + return phytium_nand_oob_write(mtd, chip, NULL, chip->oob_poi, + mtd->oobsize, page, false); +} + +static int phytium_nfc_hw_ecc_bch_write_oob(struct nand_chip *chip, int page) +{ + struct phytium_nand_chip *phytium_nand = to_phytium_nand(chip); + struct mtd_info *mtd = nand_to_mtd(chip); + u32 oob_len = mtd->oobsize - phytium_nand->ecc.length; + + return phytium_nand_oob_write(mtd, chip, NULL, chip->oob_poi, + oob_len, page, false); +} + +static int phytium_nand_hw_ecc_ctrl_init(struct mtd_info *mtd, + struct nand_ecc_ctrl *ecc) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + if ((mtd->writesize + mtd->oobsize > MAX_CHUNK_SIZE)) + return -ENOTSUPP; + + chip->ecc.algo = NAND_ECC_ALGO_BCH; + ecc->read_page_raw = phytium_nfc_hw_ecc_bch_read_page_raw; + ecc->read_page = phytium_nfc_hw_ecc_bch_read_page; + ecc->read_oob_raw = phytium_nfc_hw_ecc_bch_read_oob_raw; + ecc->read_oob = phytium_nfc_hw_ecc_bch_read_oob; + ecc->write_page_raw = phytium_nfc_hw_ecc_bch_write_page_raw; + ecc->write_page = phytium_nfc_hw_ecc_bch_write_page; + ecc->write_oob_raw = phytium_nfc_hw_ecc_bch_write_oob_raw; + ecc->write_oob = phytium_nfc_hw_ecc_bch_write_oob; + + return 0; +} + +static int phytium_nand_ecc_init(struct mtd_info *mtd, + struct nand_ecc_ctrl *ecc) +{ + int ret = 0; + + mtd_set_ooblayout(mtd, &phytium_nand_ooblayout_ops); + + switch (ecc->engine_type) { + case NAND_ECC_ENGINE_TYPE_ON_HOST: + ret = phytium_nand_hw_ecc_ctrl_init(mtd, ecc); + break; + case NAND_ECC_ENGINE_TYPE_NONE: + ecc->read_page_raw = phytium_nfc_hw_ecc_bch_read_page_raw; + ecc->read_oob_raw = phytium_nfc_hw_ecc_bch_read_oob; + ecc->write_page_raw = phytium_nfc_hw_ecc_bch_write_page_raw; + ecc->write_oob_raw = phytium_nfc_hw_ecc_bch_write_oob_raw; + ecc->read_page = ecc->read_page_raw; + ecc->read_oob = ecc->read_oob_raw; + ecc->write_page = ecc->write_page_raw; + ecc->write_oob = ecc->write_oob_raw; + break; + case NAND_ECC_ENGINE_TYPE_SOFT: + case NAND_ECC_ENGINE_TYPE_ON_DIE: + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static u8 bbt_pattern[] = {'P', 'H', 'Y', 'b', 't', '0' }; +static u8 bbt_mirror_pattern[] = {'1', 't', 'b', 'Y', 'H', 'P' }; + +static struct nand_bbt_descr bbt_main_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | + NAND_BBT_2BIT | NAND_BBT_VERSION, + .offs = 8, + .len = 6, + .veroffs = 14, + .maxblocks = 8, /* Last 8 blocks in each chip */ + .pattern = bbt_pattern +}; + +static struct nand_bbt_descr bbt_mirror_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | + NAND_BBT_2BIT | NAND_BBT_VERSION, + .offs = 8, + .len = 6, + .veroffs = 14, + .maxblocks = 8, /* Last 8 blocks in each chip */ + .pattern = bbt_mirror_pattern +}; + +static int phytium_nand_attach_chip(struct nand_chip *chip) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct phytium_nand_chip *phytium_nand = to_phytium_nand(chip); + struct phytium_nfc *nfc = to_phytium_nfc(chip->controller); + u32 value; + int ret = 0; + + if (nfc->caps->flash_bbt) + chip->bbt_options |= NAND_BBT_USE_FLASH; + + if (chip->bbt_options & NAND_BBT_USE_FLASH) { + /* + * We'll use a bad block table stored in-flash and don't + * allow writing the bad block marker to the flash. + */ + chip->bbt_options |= NAND_BBT_NO_OOB_BBM; + chip->bbt_td = &bbt_main_descr; + chip->bbt_md = &bbt_mirror_descr; + } + + if (chip->options & NAND_BUSWIDTH_16) + phytium_nand->ndcr |= NDCR0_WIDTH; + + /* + * On small page NANDs, only one cycle is needed to pass the + * column address. + */ + if (mtd->writesize <= 512) + phytium_nand->addr_cyc = 1; + else + phytium_nand->addr_cyc = 2; + + /* + * Now add the number of cycles needed to pass the row + * address. + * + * Addressing a chip using CS 2 or 3 should also need the third row + * cycle but due to inconsistance in the documentation and lack of + * hardware to test this situation, this case is not supported. + */ + if (chip->options & NAND_ROW_ADDR_3) + phytium_nand->addr_cyc += 3; + else + phytium_nand->addr_cyc += 2; + + if (nfc->caps) { + if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST) { + chip->ecc.size = nfc->caps->ecc_step_size ? + nfc->caps->ecc_step_size : + chip->ecc.size; + chip->ecc.strength = nfc->caps->ecc_strength ? + nfc->caps->ecc_strength : + chip->ecc.strength; + chip->ecc.strength = nfc->caps->ecc_strength; + chip->ecc.bytes = 7; + } else { + chip->ecc.size = 512; + chip->ecc.strength = 1; + chip->ecc.bytes = 0; + } + chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; + } + + if (nfc->caps->hw_ver == 1) { + if (chip->ecc.strength == 0x04) + phytium_nand->ndcr |= NDCR0_ECC_STREN(4); + else if (chip->ecc.strength == 0x02) + phytium_nand->ndcr |= NDCR0_ECC_STREN(2); + else + phytium_nand->ndcr |= NDCR0_ECC_STREN(0); + } else { + if (chip->ecc.strength == 0x08) + phytium_nand->ndcr |= NDCR0_ECC_STREN(7); + else if (chip->ecc.strength == 0x04) + phytium_nand->ndcr |= NDCR0_ECC_STREN(3); + else if (chip->ecc.strength == 0x02) + phytium_nand->ndcr |= NDCR0_ECC_STREN(1); + else + phytium_nand->ndcr |= NDCR0_ECC_STREN(0); + } + + value = phytium_read(nfc, NDCR0); + value &= ~NDCR0_EN; + phytium_write(nfc, NDCR0, value); + + value &= ~NDCR0_ECC_STREN(7); + value |= phytium_nand->ndcr; + phytium_write(nfc, NDCR0, value | NDCR0_EN); + + ret = phytium_nand_ecc_init(mtd, &chip->ecc); + if (ret) { + dev_err(nfc->dev, "ECC init failed: %d\n", ret); + goto out; + } + + /* + * Subpage write not available with hardware ECC, prohibit also + * subpage read as in userspace subpage access would still be + * allowed and subpage write, if used, would lead to numerous + * uncorrectable ECC errors. + */ + if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST) + chip->options |= NAND_NO_SUBPAGE_WRITE; + + /* + * We keep the MTD name unchanged to avoid breaking platforms + * where the MTD cmdline parser is used and the bootloader + * has not been updated to use the new naming scheme. + */ + if (nfc->caps->legacy_of_bindings) + mtd->name = "phytium_nand-0"; + +out: + return ret; +} + +static int phytium_nfc_setup_interface(struct nand_chip *chip, int chipnr, + const struct nand_interface_config *conf) +{ + struct phytium_nfc *nfc = to_phytium_nfc(chip->controller); + unsigned int period_ns = 2; + const struct nand_sdr_timings *sdr; + struct phytium_nfc_timings nfc_tmg; + int read_delay; + + sdr = nand_get_sdr_timings(conf); + if (IS_ERR(sdr)) + return PTR_ERR(sdr); + + nfc_tmg.tRP = TO_CYCLES(DIV_ROUND_UP(sdr->tRC_min, 2), period_ns) - 1; + nfc_tmg.tRH = nfc_tmg.tRP; + nfc_tmg.tWP = TO_CYCLES(DIV_ROUND_UP(sdr->tWC_min, 2), period_ns) - 1; + nfc_tmg.tWH = nfc_tmg.tWP; + nfc_tmg.tCS = TO_CYCLES(sdr->tCS_min, period_ns); + nfc_tmg.tCH = TO_CYCLES(sdr->tCH_min, period_ns) - 1; + nfc_tmg.tADL = TO_CYCLES(sdr->tADL_min, period_ns); + dev_info(nfc->dev, "[nfc_tmg]tRP: %d, tRH:%d, tWP:%d tWH:%d\n", + nfc_tmg.tRP, nfc_tmg.tRH, nfc_tmg.tWP, nfc_tmg.tWH); + dev_info(nfc->dev, "[nfc_tmg]tCS: %d, tCH:%d, tADL:%d\n", + nfc_tmg.tCS, nfc_tmg.tCH, nfc_tmg.tADL); + + read_delay = sdr->tRC_min >= 30000 ? + MIN_RD_DEL_CNT : MIN_RD_DEL_CNT + nfc_tmg.tRH; + + nfc_tmg.tAR = TO_CYCLES(sdr->tAR_min, period_ns); + nfc_tmg.tWHR = TO_CYCLES(max_t(int, sdr->tWHR_min, sdr->tCCS_min), + period_ns) - 2, + nfc_tmg.tRHW = TO_CYCLES(max_t(int, sdr->tRHW_min, sdr->tCCS_min), + period_ns); + dev_info(nfc->dev, "[nfc_tmg]tAR: %d, tWHR:%d, tRHW:%d\n", + nfc_tmg.tAR, nfc_tmg.tWHR, nfc_tmg.tRHW); + + nfc_tmg.tR = TO_CYCLES(sdr->tWB_max, period_ns); + + if (chipnr < 0) + return 0; + + if (nfc_tmg.tWP > 0x10) + nfc->timing_mode = ASY_MODE1; + else if (nfc_tmg.tWP < 0x0D) + nfc->timing_mode = ASY_MODE3; + + if (nfc->inter_mode == ONFI_DDR) + nfc->timing_mode = SYN_MODE3; + + phytium_nfc_default_data_interface(nfc); + + return 0; +} + +static const struct nand_controller_ops phytium_nand_controller_ops = { + .attach_chip = phytium_nand_attach_chip, + .exec_op = phytium_nfc_exec_op, + .setup_interface = phytium_nfc_setup_interface, +}; + +static void phytium_nand_chips_cleanup(struct phytium_nfc *nfc) +{ + struct phytium_nand_chip *entry, *temp; + int ret; + + list_for_each_entry_safe(entry, temp, &nfc->chips, node) { + ret = mtd_device_unregister(nand_to_mtd(&entry->chip)); + WARN_ON(ret); + nand_cleanup(&entry->chip); + list_del(&entry->node); + } +} + +static int phytium_nfc_init_dma(struct phytium_nfc *nfc) +{ + int ret; + + ret = dma_set_mask_and_coherent(nfc->dev, DMA_BIT_MASK(64)); + if (ret) + return ret; + + nfc->dsp_addr = dma_alloc_coherent(nfc->dev, PAGE_SIZE, + &nfc->dsp_phy_addr, GFP_KERNEL | GFP_DMA); + if (!nfc->dsp_addr) + return -ENOMEM; + + nfc->dma_buf = dma_alloc_coherent(nfc->dev, MAX_CHUNK_SIZE, + &nfc->dma_phy_addr, GFP_KERNEL | GFP_DMA); + if (!nfc->dma_buf) + return -ENOMEM; + + dev_info(nfc->dev, "NFC address dsp_phy_addr:%llx, dma_phy_addr:%llx\n", + nfc->dsp_phy_addr, nfc->dma_phy_addr); + + return 0; +} + +int phytium_nfc_init(struct phytium_nfc *nfc) +{ + u32 value; + + nfc->inter_mode = ASYN_SDR; + nfc->timing_mode = ASY_MODE0; + + value = phytium_read(nfc, NDCR1); + value &= (~NDCR1_SAMPL_PHASE(0xFFFF)); + value &= ~NDCR1_ECC_DATA_FIRST_EN; + value |= NDCR1_SAMPL_PHASE(1); + value |= NDCR1_ECC_BYPASS; + phytium_write(nfc, NDCR1, value); + phytium_write(nfc, ND_INTERVAL_TIME, 1); + phytium_write(nfc, NDFIFO_LEVEL0, 4); + phytium_write(nfc, NDFIFO_LEVEL1, 4); + phytium_write(nfc, NDFIFO_CLR, 1); + phytium_write(nfc, ND_ERR_CLR, 1); + + /* Configure the DMA */ + phytium_nfc_init_dma(nfc); + + + + phytium_nfc_reset(nfc); + + value = phytium_read(nfc, NDCR0); + value &= (~NDCR0_IN_MODE(3)); + value |= NDCR0_IN_MODE(nfc->inter_mode); + value |= NDCR0_EN; + + phytium_write(nfc, NDCR0, value); + + nfc_ecc_errover = 0; + + return 0; +} +EXPORT_SYMBOL_GPL(phytium_nfc_init); + +static int phytium_nand_chip_init(struct phytium_nfc *nfc) +{ + struct device *dev = nfc->dev; + struct phytium_nand_chip *phytium_nand; + struct mtd_info *mtd; + struct nand_chip *chip; + int ret; + + /* Alloc the nand chip structure */ + phytium_nand = devm_kzalloc(dev, sizeof(*phytium_nand), GFP_KERNEL); + if (!phytium_nand) + return -ENOMEM; + + phytium_nand->nsels = 1; + phytium_nand->selected_die = -1; + + chip = &phytium_nand->chip; + chip->controller = &nfc->controller; + chip->legacy.select_chip = phytium_nfc_select_chip; + phytium_nfc_default_data_interface(nfc); + + mtd = nand_to_mtd(chip); + mtd->dev.parent = dev; + mtd->owner = THIS_MODULE; + + /* + * Default to HW ECC engine mode. If the nand-ecc-mode property is given + * in the DT node, this entry will be overwritten in nand_scan_ident(). + */ + chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; + + chip->options |= NAND_BUSWIDTH_AUTO; + chip->options |= NAND_SKIP_BBTSCAN; + chip->bbt_options |= NAND_BBT_NO_OOB; + + ret = nand_scan(chip, phytium_nand->nsels); + if (ret) { + dev_err(dev, "could not scan the nand chip\n"); + goto out; + } + + if (nfc->caps->parts) { + ret = mtd_device_register(mtd, nfc->caps->parts, nfc->caps->nr_parts - 1); + } else if (dev->of_node) { + nand_set_flash_node(chip, dev->of_node); + ret = mtd_device_register(mtd, NULL, 0); + } else { + ret = -EINVAL; + } + + if (ret) { + dev_err(dev, "failed to register mtd device: %d\n", ret); + nand_cleanup(chip); + return ret; + } + + phytium_nand->ecc.length = phytium_nand_get_ecc_total(mtd, &chip->ecc); + phytium_nand->ecc.offset = mtd->oobsize - phytium_nand->ecc.length; + chip->ecc.total = phytium_nand_get_ecc_total(mtd, &chip->ecc); + + mtd_ooblayout_ecc(mtd, 0, &phytium_nand->ecc); + + dev_info(dev, "ooblayout ecc offset: %x, length: %x\n", + phytium_nand->ecc.offset, phytium_nand->ecc.length); + +out: + list_add_tail(&phytium_nand->node, &nfc->chips); + return 0; +} + +int phytium_nand_init(struct phytium_nfc *nfc) +{ + int ret; + + nand_controller_init(&nfc->controller); + nfc->controller.ops = &phytium_nand_controller_ops; + INIT_LIST_HEAD(&nfc->chips); + + /* Init the controller and then probe the chips */ + ret = phytium_nfc_init(nfc); + if (ret) + goto out; + + ret = phytium_nand_chip_init(nfc); + if (ret) + goto out; + + spin_lock_init(&nfc->spinlock); + +out: + return ret; +} +EXPORT_SYMBOL_GPL(phytium_nand_init); + +int phytium_nand_remove(struct phytium_nfc *nfc) +{ + phytium_nand_chips_cleanup(nfc); + + return 0; +} +EXPORT_SYMBOL_GPL(phytium_nand_remove); + +static int phytium_nfc_wait_ndrun(struct nand_chip *chip) +{ + struct phytium_nfc *nfc = to_phytium_nfc(chip->controller); + int ret = 0; + u32 val; + + ret = readl_relaxed_poll_timeout(nfc->regs + NDSR, val, + (val & NDSR_RB) == 0, + 0, 100 * 1000); + if (ret) { + dev_err(nfc->dev, "Timeout on NAND controller run mode\n"); + ret = -EAGAIN; + } + + return ret; +} + +int phytium_nand_prepare(struct phytium_nfc *nfc) +{ + struct phytium_nand_chip *chip = NULL; + + list_for_each_entry(chip, &nfc->chips, node) + phytium_nfc_wait_ndrun(&chip->chip); + + return 0; +} +EXPORT_SYMBOL_GPL(phytium_nand_prepare); + +int phytium_nand_resume(struct phytium_nfc *nfc) +{ + nfc->selected_chip = NULL; + phytium_nfc_init(nfc); + phytium_nfc_default_data_interface(nfc); + + return 0; +} +EXPORT_SYMBOL_GPL(phytium_nand_resume); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Phytium NAND controller platform driver"); +MODULE_AUTHOR("Zhu Mingshuai "); diff --git a/target/linux/phytium/files-5.10/drivers/mtd/nand/raw/phytium_nand.h b/target/linux/phytium/files-5.10/drivers/mtd/nand/raw/phytium_nand.h new file mode 100644 index 000000000..963d555a2 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/mtd/nand/raw/phytium_nand.h @@ -0,0 +1,449 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Phytium NAND flash controller driver + * + * Copyright (c) 2020-2023 Phytium Technology Co., Ltd. + */ +#ifndef PHYTIUM_NAND_H +#define PHYTIUM_NAND_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* NFC does not support transfers of larger chunks at a time */ +#define MAX_PAGE_NUM 16 +#define MAX_CHUNK_SIZE ((1024 + 76) * 16) + +#define POLL_PERIOD 0 +#define POLL_TIMEOUT 100000 +/* Interrupt maximum wait period in ms */ +#define IRQ_TIMEOUT 1000 + +/* Latency in clock cycles between SoC pins and NFC logic */ +#define MIN_RD_DEL_CNT 3 + +#define PHYTIUM_NFC_ADDR_MAX_LEN 5 +#define PHYTIUM_NFC_DSP_SIZE 16 + +/* NAND controller flash control register */ +#define NDCR0 0x00 +#define NDCR0_EN BIT(0) +#define NDCR0_WIDTH BIT(1) +#define NDCR0_IN_MODE(x) (min_t(u32, x, 0x3) << 2) +#define NDCR0_ECC_EN BIT(4) +#define NDCR0_ECC_STREN(x) (min_t(u32, x, 0x7) << 5) +#define NDCR0_SPARE_EN BIT(8) +#define NDCR0_SPARE_SIZE(x) (min_t(u32, x, 0xFFF) << 9) +#define NDCR0_GENERIC_FIELDS_MASK + +#define NDCR1 0x04 +#define NDCR1_SAMPL_PHASE(x) min_t(u32, x, 0xFFFF) +#define NDCR1_ECC_DATA_FIRST_EN BIT(16) +#define NDCR1_RB_SHARE_EN BIT(17) +#define NDCR1_ECC_BYPASS BIT(18) + +#define NDAR0 0x08 + +#define NDAR1 0x0C +#define NDAR1_H8(x) min_t(u32, x, 0xFF) +#define NDAR1_DMA_EN BIT(8) +#define NDAR1_EMPTY(x) (min_t(u32, x, 0x7F) << 9) +#define NDAR1_DMA_RLEN(x) (min_t(u32, x, 0xFF) << 9) +#define NDAR1_DMA_WLEN(x) (min_t(u32, x, 0xFF) << 9) + +#define NDTR0 0x10 +#define NDTR0_TCS_TCLS(x) (min_t(u32, x, 0xFFFF) << 0) +#define NDTR0_TCLS_TWP(x) (min_t(u32, x, 0xFFFF) << 16) + +#define NDTR1 0x14 +#define NDTR1_TWH(x) (min_t(u32, x, 0xFFFF) << 0) +#define NDTR1_TWP(x) (min_t(u32, x, 0xFFFF) << 16) + +#define NDTR2 0x18 +#define NDTR2_TCH_TCLH(x) (min_t(u32, x, 0xFFFF) << 0) +#define NDTR2_TCLH_TWH(x) (min_t(u32, x, 0xFFFF) << 16) + +#define NDTR3 0x1c +#define NDTR3_TDQ_EN(x) (min_t(u32, x, 0xFFFF) << 0) +#define NDTR3_TCH_TWH(x) (min_t(u32, x, 0xFFFF) << 16) + +#define NDTR4 0x20 +#define NDTR4_TWHR_SMX(x) (min_t(u32, x, 0xFFFF) << 0) +#define NDTR4_TREH(x) (min_t(u32, x, 0xFFFF) << 16) + +#define NDTR5 0x24 +#define NDTR5_TRC(x) (min_t(u32, x, 0xFFFF) << 0) +#define NDTR5_TADL_SMX(x) (min_t(u32, x, 0xFFFF) << 16) + +#define NDTR6 0x28 +#define NDTR6_TCAD_TCS_SMX(x) (min_t(u32, x, 0xFFFF) << 0) +#define NDTR6_RES(x) (min_t(u32, x, 0xFFFF) << 16) + +#define NDTR7 0x2c +#define NDTR7_TCK(x) (min_t(u32, x, 0xFFFF) << 0) +#define NDTR7_TDQ_EN(x) (min_t(u32, x, 0xFFFF) << 16) + +#define NDTR8 0x30 +#define NDTR8_TCAD_TCK_SMX(x) (min_t(u32, x, 0xFFFF) << 0) +#define NDTR8_HF_TCK(x) (min_t(u32, x, 0xFFFF) << 16) + +#define NDTR9 0x34 +#define NDTR9_TWHR(x) (min_t(u32, x, 0xFFFF) << 0) +#define NDTR9_TCCS_TCALS_SMX(x) (min_t(u32, x, 0xFFFF) << 16) + +#define NDTR10 0x38 +#define NDTR10_TCK(x) (min_t(u32, x, 0xFFFF) << 0) +#define NDTR10_MTCK(x) (min_t(u32, x, 0xFFFF) << 16) + +#define NDTR11 0x3c +#define NDTR11_TCK_TCALS(x) (min_t(u32, x, 0xFFFF) << 16) +#define NDTR11_RES(x) (min_t(u32, x, 0xFFFF) << 16) + +#define NDTR12 0x40 +#define NDTR12_TWRCK(x) (min_t(u32, x, 0xFFFF) << 0) +#define NDTR12_RES(x) (min_t(u32, x, 0xFFFF) << 16) + +#define NDTR13 0x44 +#define NDTR13_TWRHCA(x) (min_t(u32, x, 0xFFFF) << 0) +#define NDTR13_TRLCA(x) (min_t(u32, x, 0xFFFF) << 16) + +#define NDTR14 0x48 +#define NDTR14_TWRHCE(x) (min_t(u32, x, 0xFFFF) << 0) +#define NDTR14_RES(x) (min_t(u32, x, 0xFFFF) << 16) + +#define NDTR15 0x4c +#define NDTR15_TCDQSS_TWPRE_TDS(x) (min_t(u32, x, 0xFFFF) << 0) +#define NDTR15_HFTDSC(x) (min_t(u32, x, 0xFFFF) << 16) + +#define NDTR16 0x50 +#define NDTR16_TWPST_TDH(x) (min_t(u32, x, 0xFFFF) << 0) +#define NDTR16_TWPSTH(x) (min_t(u32, x, 0xFFFF) << 16) + +#define NDTR17 0x54 +#define NDTR17_TCS_TRPRE(x) (min_t(u32, x, 0xFFFF) << 0) +#define NDTR17_TRELDQS(x) (min_t(u32, x, 0xFFFF) << 16) + +#define NDTR18 0x58 +#define NDTR18_TRPST_TDQSRE(x) (min_t(u32, x, 0xFFFF) << 0) +#define NDTR18_RES(x) (min_t(u32, x, 0xFFFF) << 16) + +#define NDFIFO 0x5c +#define NDFIFO_REV (min_t(u32, x, 0) << 12) +#define NDFIFO_FULL BIT(11) +#define NDFIFO_EMP BIT(10) +#define NDFIFO_CNT(x) (min_t(u32, x, 0x3F) << 0) + +#define ND_INTERVAL_TIME 0x60 +#define NDCMD_INTERVAL_TIME 0x64 +#define NDFIFO_TIMEOUT 0x68 +#define NDFIFO_LEVEL0 0x6c +#define NDFIFO_LEVEL1 0x70 +#define NDWP 0x74 +#define NDFIFO_CLR 0x78 + +#define NDSR 0x7c +#define NDSR_BUSY BIT(0) +#define NDSR_DMA_BUSY BIT(1) +#define NDSR_DMA_PGFINISH BIT(2) +#define NDSR_DMA_FINISH BIT(3) +#define NDSR_FIFO_EMP BIT(4) +#define NDSR_FIFO_FULL BIT(5) +#define NDSR_FIFO_TIMEOUT BIT(6) +#define NDSR_CS(x) (min_t(u32, x, 0xF) << 7) +#define NDSR_CMD_PGFINISH BIT(11) +#define NDSR_PG_PGFINISH BIT(12) +#define NDSR_RE BIT(13) +#define NDSR_DQS BIT(14) +#define NDSR_RB BIT(15) +#define NDSR_ECC_BUSY BIT(16) +#define NDSR_ECC_FINISH BIT(17) +#define NDSR_ECC_RIGHT BIT(18) +#define NDSR_ECC_ERR BIT(19) +#define NDSR_ECC_ERROVER BIT(20) +#define NDSR_AXI_DSP_ERR BIT(21) +#define NDSR_AXI_RD_ERR BIT(22) +#define NDSR_AXI_WR_ERR BIT(23) +#define NDSR_RB_STATUS(x) (min_t(u32, x, 0xF) << 24) +#define NDSR_PROT_ERR BIT(28) +#define NDSR_ECC_BYPASS BIT(29) + +#define NDIR_MASK 0x80 +#define NDIR_BUSY_MASK BIT(0) +#define NDIR_DMA_BUSY_MASK BIT(1) +#define NDIR_DMA_PGFINISH_MASK BIT(2) +#define NDIR_DMA_FINISH_MASK BIT(3) +#define NDIR_FIFO_EMP_MASK BIT(4) +#define NDIR_FIFO_FULL_MASK BIT(5) +#define NDIR_FIFO_TIMEOUT_MASK BIT(6) +#define NDIR_CMD_FINISH_MASK BIT(7) +#define NDIR_PGFINISH_MASK BIT(8) +#define NDIR_RE_MASK BIT(9) +#define NDIR_DQS_MASK BIT(10) +#define NDIR_RB_MASK BIT(11) +#define NDIR_ECC_FINISH_MASK BIT(12) +#define NDIR_ECC_ERR_MASK BIT(13) + +#define NDIR 0x84 +#define NDIR_ALL_INT(x) GENMASK(x, 0) +#define NDIR_BUSY BIT(0) +#define NDIR_DMA_BUSY BIT(1) +#define NDIR_DMA_PGFINISH BIT(2) +#define NDIR_DMA_FINISH BIT(3) +#define NDIR_FIFO_EMP BIT(4) +#define NDIR_FIFO_FULL BIT(5) +#define NDIR_FIFO_TIMEOUT BIT(6) +#define NDIR_CMD_FINISH BIT(7) +#define NDIR_PGFINISH BIT(8) +#define NDIR_RE BIT(9) +#define NDIR_DQS BIT(10) +#define NDIR_RB BIT(11) +#define NDIR_ECC_FINISH BIT(12) +#define NDIR_ECC_ERR BIT(13) + +#define ND_DEBUG 0x88 + +#define ND_ERR_CLR 0x8c +#define ND_DSP_ERR_CLR BIT(0) +#define ND_AXI_RD_ERR_CLR BIT(1) +#define ND_AXI_WR_ERR_CLR BIT(2) +#define ND_ECC_ERR_CLR BIT(3) + +#define NDCR_PAGE_SZ(x) (x >= 2048 ? BIT(24) : 0) + +enum nand_inter_pro { + NAND_ONFI, + NAND_JEDEC, + NAND_OTHER, +}; + +enum nand_inter_mode { + ASYN_SDR, + ONFI_DDR, + TOG_ASYN_DDR, +}; + +enum asy_timing_mode { + ASY_MODE0, + ASY_MODE1, + ASY_MODE2, + ASY_MODE3, + ASY_MODE4, +}; + +enum onfi_syn_timing_mode { + SYN_MODE0 = 0x10, + SYN_MODE1, + SYN_MODE2, + SYN_MODE3, + SYN_MODE4, +}; + +/** + * NAND controller timings expressed in NAND Controller clock cycles + * + * @tRP: ND_nRE pulse width + * @tRH: ND_nRE high duration + * @tWP: ND_nWE pulse time + * @tWH: ND_nWE high duration + * @tCS: Enable signal setup time + * @tCH: Enable signal hold time + * @tADL: Address to write data delay + * @tAR: ND_ALE low to ND_nRE low delay + * @tWHR: ND_nWE high to ND_nRE low for status read + * @tRHW: ND_nRE high duration, read to write delay + * @tR: ND_nWE high to ND_nRE low for read + */ +struct phytium_nfc_timings { + u16 tRP; + u16 tRH; + u16 tWP; /* NDTR1_TWP */ + u16 tWH; /* NDTR1_TWH */ + u16 tCS; + u16 tCH; + u16 tADL; + u16 tAR; + u16 tWHR; + u16 tRHW; + u16 tR; +}; + +/** + * NAND chip structure: stores NAND chip device related information + * + * @chip: Base NAND chip structure + * @node: Used to store NAND chips into a list + * @ndcr: Controller register value for this NAND chip + * @ndtr0: Timing registers 0 value for this NAND chip + * @ndtr1: Timing registers 1 value for this NAND chip + * @selected_die: Current active CS + * @nsels: Number of CS lines required by the NAND chip + */ +struct phytium_nand_chip { + struct nand_chip chip; + struct list_head node; + u32 ndcr; + u32 ndtr0; + u32 ndtr1; + int addr_cyc; + int selected_die; + unsigned int nsels; + struct mtd_oob_region ecc; +}; + +/** + * NAND controller capabilities for distinction between compatible strings + * + * @max_cs_nb: Number of Chip Select lines available + * @max_rb_nb: Number of Ready/Busy lines available + * @legacy_of_bindings: Indicates if DT parsing must be done using the old + * fashion way + * @flash_bbt: + * @ecc_strength: + * @ecc_step_size: + * @parts: + * @nr_parts: + */ +struct phytium_nfc_caps { + unsigned int hw_ver; + unsigned int int_mask_bits; + unsigned int max_cs_nb; + unsigned int max_rb_nb; + bool legacy_of_bindings; + bool flash_bbt; + int ecc_strength; + int ecc_step_size; + struct mtd_partition *parts; + unsigned int nr_parts; +}; + +/** + * NAND controller structure: stores Marvell NAND controller information + * + * @controller: Base controller structure + * @dev: Parent device (used to print error messages) + * @regs: NAND controller registers + * @reg_clk: Regiters clock + * @complete: Completion object to wait for NAND controller events + * @chips: List containing all the NAND chips attached to + * this NAND controller + * @caps: NAND controller capabilities for each compatible string + * @dma_buf: 32-bit aligned buffer for DMA transfers (NFCv1 only) + */ +struct phytium_nfc { + struct nand_controller controller; + struct device *dev; + void __iomem *regs; + int irq; + struct list_head chips; + struct nand_chip *selected_chip; + struct phytium_nfc_caps *caps; + + void *dsp_addr; + dma_addr_t dsp_phy_addr; + + void *dma_buf; + u32 dma_offset; + dma_addr_t dma_phy_addr; + + enum nand_inter_pro inter_pro; + enum nand_inter_mode inter_mode; + u32 timing_mode; + + spinlock_t spinlock; +}; + +/** + * Derives a duration in numbers of clock cycles. + * + * @ps: Duration in pico-seconds + * @period_ns: Clock period in nano-seconds + * + * Convert the duration in nano-seconds, then divide by the period and + * return the number of clock periods. + */ +#define TO_CYCLES(ps, period_ns) (DIV_ROUND_UP(ps / 1000, period_ns)) +#define TO_CYCLES64(ps, period_ns) (DIV_ROUND_UP_ULL(div_u64(ps, 1000), \ + period_ns)) + +struct phytium_nfc_cmd_ctrl { + u16 csel:4; + u16 dbc:1; + u16 addr_cyc:3; + u16 nc:1; +#define TYPE_RESET 0x00 +#define TYPE_SET_FTR 0x01 +#define TYPE_GET_FTR 0x02 +#define TYPE_READ_ID 0x03 +#define TYPE_PAGE_PRO 0x04 +#define TYPE_ERASE 0x05 +#define TYPE_READ 0x06 +#define TYPE_TOGGLE 0x07 +#define TYPE_READ_PARAM 0x02 +#define TYPE_READ_STATUS 0x03 +#define TYPE_CH_READ_COL 0x03 +#define TYPE_CH_ROW_ADDR 0x01 +#define TYPE_CH_WR_COL 0x01 + u16 cmd_type:4; + u16 dc:1; + u16 auto_rs:1; + u16 ecc_en:1; +}; + +/** + * NAND driver structure filled during the parsing of the ->exec_op() subop + * subset of instructions. + * + * @cle_ale_delay_ns: Optional delay after the last CMD or ADDR cycle + * @rdy_timeout_ms: Timeout for waits on Ready/Busy pin + * @rdy_delay_ns: Optional delay after waiting for the RB pin + * @data_delay_ns: Optional delay after the data xfer + * @data_instr_idx: Index of the data instruction in the subop + * @data_instr: Pointer to the data instruction in the subop + */ +struct phytium_nfc_op { + u8 cmd[2]; + union { + u16 ctrl; + struct phytium_nfc_cmd_ctrl nfc_ctrl; + } cmd_ctrl; + u8 addr[PHYTIUM_NFC_ADDR_MAX_LEN]; + u16 page_cnt; + u8 mem_addr_first[PHYTIUM_NFC_ADDR_MAX_LEN]; + + u32 cmd_len; + u32 addr_len; + + u32 cle_ale_delay_ns; + u32 rdy_timeout_ms; + u32 rdy_delay_ns; + u32 data_delay_ns; + u32 data_instr_idx; + struct nand_op_instr *data_instr; +} __attribute__ ((__packed__)); + +#define TIMING_ASY_NUM 12 +#define TIMING_SYN_NUM 14 +#define TIMING_TOG_NUM 12 + +#define TMP_DMA_DEBUG 0 /* Temporary dma space */ + +int phytium_nand_init(struct phytium_nfc *nfc); +int phytium_nand_remove(struct phytium_nfc *nfc); +int phytium_nand_prepare(struct phytium_nfc *nfc); +int phytium_nand_suspend(struct phytium_nfc *nfc); +int phytium_nand_resume(struct phytium_nfc *nfc); + +irqreturn_t phytium_nfc_isr(int irq, void *dev_id); + +#endif /* NAND_PHYTIUM_NAND_H */ diff --git a/target/linux/phytium/files-5.10/drivers/mtd/nand/raw/phytium_nand_pci.c b/target/linux/phytium/files-5.10/drivers/mtd/nand/raw/phytium_nand_pci.c new file mode 100644 index 000000000..6ce76dd15 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/mtd/nand/raw/phytium_nand_pci.c @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PCI driver for Phytium NAND flash controller + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ +#include +#include +#include + +#include "phytium_nand.h" + +#define DRV_NAME "phytium_nand_pci" + +static struct mtd_partition partition_info[] = { + { + .name = "Flash partition 1", + .offset = 0x0000000, + .size = 0x4000000 }, + { + .name = "Flash partition 2", + .offset = 0x4000000, + .size = 0x8000000 }, + { + .name = "Flash partition 3", + .offset = 0x8000000, + .size = 0x10000000 }, + { + .name = "Flash partition 4", + .offset = 0x10000000, + .size = 0x12000000 }, + { + .name = "Flash partition 5", + .offset = 0x12000000, + .size = 0x14000000 }, +}; + +static struct phytium_nfc_caps x100_nfc_caps = { + .hw_ver = 1, + .int_mask_bits = 13, + .max_cs_nb = 2, + .max_rb_nb = 1, + .legacy_of_bindings = true, + .ecc_strength = 4, + .ecc_step_size = 512, + .nr_parts = 5, + .parts = partition_info, +}; + +static int phytium_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pid) +{ + struct phytium_nfc *nfc; + int ret; + + ret = pcim_enable_device(pdev); + if (ret) + return ret; + + ret = pcim_iomap_regions(pdev, 0x1, pci_name(pdev)); + if (ret) { + dev_err(&pdev->dev, "I/O memory remapping failed\n"); + return ret; + } + + pci_set_master(pdev); + pci_try_set_mwi(pdev); + + ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(64)); + if (ret) + return ret; + + ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)); + if (ret) + return ret; + + nfc = devm_kzalloc(&pdev->dev, sizeof(struct phytium_nfc), + GFP_KERNEL); + if (!nfc) + return -ENOMEM; + + nfc->dev = &pdev->dev; + nfc->regs = pcim_iomap_table(pdev)[0]; + nfc->irq = pdev->irq; + nfc->caps = &x100_nfc_caps; + + ret = devm_request_irq(nfc->dev, nfc->irq, phytium_nfc_isr, + IRQF_SHARED, "phytium-nfc-pci", nfc); + if (ret) { + dev_err(nfc->dev, "Failed to register NFC interrupt.\n"); + return ret; + } + + ret = phytium_nand_init(nfc); + if (ret) + return ret; + + pci_set_drvdata(pdev, nfc); + + return ret; +} + +static void phytium_pci_remove(struct pci_dev *pdev) +{ + struct phytium_nfc *nfc = pci_get_drvdata(pdev); + int ret; + + ret = phytium_nand_remove(nfc); + if (ret) + dev_warn(&pdev->dev, "can't remove device properly: %d\n", ret); +} + +static int __maybe_unused phytium_nfc_prepare(struct device *dev) +{ + struct pci_dev *pci = to_pci_dev(dev); + struct phytium_nfc *nfc = pci_get_drvdata(pci); + int ret; + + ret = phytium_nand_prepare(nfc); + + return 0; +} + +static int __maybe_unused phytium_nfc_resume(struct device *dev) +{ + struct pci_dev *pci = to_pci_dev(dev); + struct phytium_nfc *nfc = pci_get_drvdata(pci); + int ret; + + ret = phytium_nand_resume(nfc); + + return ret; +} + +static const struct dev_pm_ops phytium_pci_dev_pm_ops = { + .prepare = phytium_nfc_prepare, + .resume = phytium_nfc_resume, +}; + +static const struct pci_device_id phytium_pci_id_table[] = { + { PCI_VDEVICE(PHYTIUM, 0xdc29) }, + { } +}; +MODULE_DEVICE_TABLE(pci, phytium_pci_id_table); + +static struct pci_driver phytium_pci_driver = { + .name = DRV_NAME, + .id_table = phytium_pci_id_table, + .probe = phytium_pci_probe, + .remove = phytium_pci_remove, + .driver = { + .pm = &phytium_pci_dev_pm_ops, + }, +}; +module_pci_driver(phytium_pci_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("PCI driver for Phytium NAND controller"); +MODULE_AUTHOR("Zhu Mingshuai "); diff --git a/target/linux/phytium/files-5.10/drivers/mtd/nand/raw/phytium_nand_plat.c b/target/linux/phytium/files-5.10/drivers/mtd/nand/raw/phytium_nand_plat.c new file mode 100644 index 000000000..b3fde07b4 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/mtd/nand/raw/phytium_nand_plat.c @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Core driver for Phytium NAND flash controller + * + * Copyright (c) 2020-2023 Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "phytium_nand.h" + +#define DRV_NAME "phytium_nand_plat" + +static int phytium_nfc_plat_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *r; + struct phytium_nfc *nfc; + unsigned int ecc_strength; + unsigned int ecc_step_size; + int ret; + + nfc = devm_kzalloc(&pdev->dev, sizeof(struct phytium_nfc), + GFP_KERNEL); + if (!nfc) + return -ENOMEM; + + nfc->dev = dev; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + nfc->regs = devm_ioremap_resource(dev, r); + if (IS_ERR(nfc->regs)) + return PTR_ERR(nfc->regs); + + dev_info(nfc->dev, "NFC register address :%p, phy address:%llx\n", + nfc->regs, r->start); + + nfc->irq = platform_get_irq(pdev, 0); + if (nfc->irq < 0) { + dev_err(dev, "failed to retrieve irq\n"); + return nfc->irq; + } + + ret = devm_request_irq(dev, nfc->irq, phytium_nfc_isr, 0, + "phytium-nfc-plat", nfc); + if (ret) { + dev_err(nfc->dev, "Failed to register NFC interrupt.\n"); + return ret; + } + + nfc->caps = devm_kzalloc(dev, sizeof(struct phytium_nfc_caps), GFP_KERNEL); + if (!nfc->caps) + return -ENOMEM; + + /* Currently hard-coded parameters */ + nfc->caps->hw_ver = 2; + nfc->caps->int_mask_bits = 17; + nfc->caps->max_cs_nb = 4; + nfc->caps->max_rb_nb = 4; + nfc->caps->nr_parts = 0; + nfc->caps->parts = NULL; + + device_property_read_u32(&pdev->dev, "nand-ecc-strength", &ecc_strength); + nfc->caps->ecc_strength = ecc_strength ? ecc_strength : 8; + + device_property_read_u32(&pdev->dev, "nand-ecc-step-size", &ecc_step_size); + nfc->caps->ecc_step_size = ecc_step_size ? ecc_step_size : 512; + + ret = phytium_nand_init(nfc); + if (ret) + return ret; + + platform_set_drvdata(pdev, nfc); + + return ret; +} + +static int phytium_nfc_plat_remove(struct platform_device *pdev) +{ + struct phytium_nfc *nfc = platform_get_drvdata(pdev); + + return phytium_nand_remove(nfc); +} + +static int __maybe_unused phytium_nfc_plat_prepare(struct device *dev) +{ + struct phytium_nfc *nfc = dev_get_drvdata(dev); + + return phytium_nand_prepare(nfc); +} + +static int __maybe_unused phytium_nfc_plat_resume(struct device *dev) +{ + struct phytium_nfc *nfc = dev_get_drvdata(dev); + int ret; + + ret = phytium_nand_resume(nfc); + + return ret; +} + +static const struct dev_pm_ops phytium_dev_pm_ops = { + .prepare = phytium_nfc_plat_prepare, + .resume = phytium_nfc_plat_resume, +}; + +#ifdef CONFIG_OF +static const struct of_device_id phytium_nfc_of_ids[] = { + { .compatible = "phytium,nfc", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, phytium_nfc_of_ids); +#endif + +static struct platform_driver phytium_nfc_plat_driver = { + .driver = { + .name = DRV_NAME, + .of_match_table = phytium_nfc_of_ids, + .pm = &phytium_dev_pm_ops, + }, + .probe = phytium_nfc_plat_probe, + .remove = phytium_nfc_plat_remove, +}; +module_platform_driver(phytium_nfc_plat_driver) + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Phytium NAND controller Platform driver"); +MODULE_AUTHOR("Zhu Mingshuai "); diff --git a/target/linux/phytium/files-5.10/drivers/mtd/spi-nor/boya.c b/target/linux/phytium/files-5.10/drivers/mtd/spi-nor/boya.c new file mode 100644 index 000000000..db8c0c218 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/mtd/spi-nor/boya.c @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005, Intec Automation Inc. + * Copyright (C) 2014, Freescale Semiconductor, Inc. + */ + +#include + +#include "core.h" + +static const struct flash_info boya_parts[] = { + /* Boya Microelectronics Memory Series */ + { "by25q64as", INFO(0x684017, 0, 64 * 1024, 128, SECT_4K) }, + { "by25q128as", INFO(0x684018, 0, 64 * 1024, 256, SECT_4K) }, +}; + +const struct spi_nor_manufacturer spi_nor_boya = { + .name = "boya", + .parts = boya_parts, + .nparts = ARRAY_SIZE(boya_parts), +}; diff --git a/target/linux/phytium/files-5.10/drivers/mtd/spi-nor/controllers/phytium-quadspi.c b/target/linux/phytium/files-5.10/drivers/mtd/spi-nor/controllers/phytium-quadspi.c new file mode 100644 index 000000000..67937d8b6 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/mtd/spi-nor/controllers/phytium-quadspi.c @@ -0,0 +1,1024 @@ +/* + * Phytium SPI core controller driver. + * + * Copyright (c) 2019-2023 Phytium Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define QSPI_FLASH_CAP_REG 0x000 +#define QSPI_RD_CFG_REG 0x004 +#define QSPI_WR_CFG_REG 0x008 +#define QSPI_FLUSH_REG 0x00C +#define QSPI_CMD_PORT_REG 0x010 +#define QSPI_ADDR_PORT_REG 0x014 +#define QSPI_HD_PORT_REG 0x018 +#define QSPI_LD_PORT_REG 0x01C +#define QSPI_FUN_SET_REG 0x020 +#define QSPI_WIP_REG 0x024 +#define QSPI_WP_REG 0x028 +#define QSPI_MODE_REG 0x02C + +#define QSPI_FLASH_CAP_NUM_SHIFT 3 +#define QSPI_FLASH_CAP_NUM_MASK (0x3 << QSPI_FLASH_CAP_NUM_SHIFT) +#define QSPI_FLASH_CAP_CAP_SHIFT 0 +#define QSPI_FLASH_CAP_CAP_MASK (0x7 << QSPI_FLASH_CAP_CAP_SHIFT) + +#define QSPI_RD_CFG_RD_CMD_SHIFT 24 +#define QSPI_RD_CFG_RD_CMD_MASK (0xFF << QSPI_RD_CFG_RD_CMD_SHIFT) +#define QSPI_RD_CFG_RD_THROUGH_SHIFT 23 +#define QSPI_RD_CFG_RD_THROUGH_MASK (0x01 << QSPI_RD_CFG_RD_THROUGH_SHIFT) +#define QSPI_RD_CFG_RD_TRANSFER_SHIFT 20 +#define QSPI_RD_CFG_RD_TRANSFER_MASK (0x07 << QSPI_RD_CFG_RD_TRANSFER_SHIFT) +#define QSPI_RD_CFG_RD_ADDR_SEL_SHIFT 19 +#define QSPI_RD_CFG_RD_ADDR_SEL_MASK (0x1 << QSPI_RD_CFG_RD_ADDR_SEL_SHIFT) +#define QSPI_RD_CFG_RD_LATENCY_SHIFT 18 +#define QSPI_RD_CFG_RD_LATENCY_MASK (0x1 << QSPI_RD_CFG_RD_LATENCY_SHIFT) +#define QSPI_RD_CFG_MODE_BYTE_SHIFT 17 +#define QSPI_RD_CFG_MODE_BYTE_MASK (0x1 << QSPI_RD_CFG_MODE_BYTE_SHIFT) +#define QSPI_RD_CFG_CMD_SIGN_SHIFT 9 +#define QSPI_RD_CFG_CMD_SIGN_MASK (0xFF << QSPI_RD_CFG_CMD_SIGN_SHIFT) +#define QSPI_RD_CFG_DUMMY_SHIFT 4 +#define QSPI_RD_CFG_DUMMY_MASK (0x1F << QSPI_RD_CFG_DUMMY_SHIFT) +#define QSPI_RD_CFG_D_BUFFER_SHIFT 3 +#define QSPI_RD_CFG_D_BUFFER_MASK (0x1 << QSPI_RD_CFG_D_BUFFER_SHIFT) +#define QSPI_RD_CFG_RD_SCK_SEL_SHIFT 0 +#define QSPI_RD_CFG_RD_SCK_SEL_MASK (0x3 << QSPI_RD_CFG_RD_SCK_SEL_SHIFT) + +#define QSPI_WR_CFG_WR_CMD_SHIFT 24 +#define QSPI_WR_CFG_WR_CMD_MASK (0xFF << QSPI_WR_CFG_WR_CMD_SHIFT) +#define QSPI_WR_CFG_WR_WAIT_SHIFT 9 +#define QSPI_WR_CFG_WR_WAIT_MASK (0x01 << QSPI_WR_CFG_WR_WAIT_SHIFT) +#define QSPI_WR_CFG_WR_THROUGH_SHIFT 8 +#define QSPI_WR_CFG_WR_THROUGH_MAS (0x01 << QSPI_WR_CFG_WR_THROUGH_SHIFT) +#define QSPI_WR_CFG_WR_TRANSFER_SHIFT 5 +#define QSPI_WR_CFG_WR_TRANSFER_MASK (0X7 << QSPI_WR_CFG_WR_TRANSFER_SHIFT) +#define QSPI_WR_CFG_WR_ADDR_SEL_SHIFT 4 +#define QSPI_WR_CFG_WR_ADDR_SEL_MASK (0x1 << QSPI_WR_CFG_WR_ADDR_SEL_SHIFT) +#define QSPI_WR_CFG_WR_MODE_SHIFT 3 +#define QSPI_WR_CFG_WR_MODE (0x01 << QSPI_WR_CFG_WR_MODE_SHIFT) +#define QSPI_WR_CFG_WR_SCK_SEL_SHIFT 0 +#define QSPI_WR_CFG_WR_SCK_SEL_MASK (0x7 << QSPI_WR_CFG_WR_SCK_SEL_SHIFT) + +#define QSPI_FLUSH_EN (0x1 << 0) + +#define QSPI_CMD_PORT_CMD_SHIFT 24 +#define QSPI_CMD_PORT_CMD_MASK (0xFF << QSPI_CMD_PORT_CMD_SHIFT) +#define QSPI_CMD_PORT_WAIT_SHIFT 22 +#define QSPI_CMD_PORT_WAIT_MASK (0x1 << QSPI_CMD_PORT_WAIT_SHIFT) +#define QSPI_CMD_PORT_THROUGH_SHIFT 21 +#define QSPI_CMD_PORT_THROUGH_MASK (0x1 << QSPI_CMD_PORT_THROUGH_SHIFT) +#define QSPI_CMD_PORT_CS_SHIFT 19 +#define QSPI_CMD_PORT_CS_MASK (0x3 << QSPI_CMD_PORT_CS_SHIFT) +#define QSPI_CMD_PORT_TRANSFER_SHIFT 16 +#define QSPI_CMD_PORT_TRANSFER_MASK (0x7 << QSPI_CMD_PORT_TRANSFER_SHIFT) +#define QSPI_CMD_PORT_CMD_ADDR_SHIFT 15 +#define QSPI_CMD_PORT_CMD_ADDR_MASK (0x1 << QSPI_CMD_PORT_CMD_ADDR_SHIFT) +#define QSPI_CMD_PORT_LATENCY_SHIFT 14 +#define QSPI_CMD_PORT_LATENCY_MASK (0x1 << QSPI_CMD_PORT_LATENCY_SHIFT) +#define QSPI_CMD_PORT_DATA_TRANSFER_SHIFT 13 +#define QSPI_CMD_PORT_DATA_TRANSFER_MASK (0x1 << 13) +#define QSPI_CMD_PORT_SEL_SHIFT 12 +#define QSPI_CMD_PORT_SEL_MASK (0x1 << QSPI_CMD_PORT_SEL_SHIFT) +#define QSPI_CMD_PORT_DUMMY_SHIFT 7 +#define QSPI_CMD_PORT_DUMMY_MASK (0x1F << QSPI_CMD_PORT_DUMMY_SHIFT) +#define QSPI_CMD_PORT_DUMMY(x) (((x) << QSPI_CMD_PORT_DUMMY_SHIFT) & QSPI_CMD_PORT_DUMMY_MASK) +#define QSPI_CMD_PORT_P_BUFFER_SHIFT 6 +#define QSPI_CMD_PORT_P_BUFFER_MASK (0x1 << QSPI_CMD_PORT_P_BUFFER_SHIFT) +#define QSPI_CMD_PORT_RW_NUM_SHIFT 3 +#define QSPI_CMD_PORT_RW_NUM_MASK (0x7 << QSPI_CMD_PORT_RW_NUM_SHIFT) +#define QSPI_CMD_PORT_SCK_SEL_SHIFT 0 +#define QSPI_CMD_PORT_SCK_SEL_MASK (0x7 << QSPI_CMD_PORT_SCK_SEL_SHIFT) + +#define QSPI_FUN_SET_HOLD_SHIFT 24 +#define QSPI_FUN_SET_HOLD_MASK (0xFF << QSPI_FUN_SET_HOLD_SHIFT) +#define QSPI_FUN_SET_SETUP_SHIFT 16 +#define QSPI_FUN_SET_SETUP_MASK (0xFF << QSPI_FUN_SET_SETUP_SHIFT) +#define QSPI_FUN_SET_DELAY_SHIFT 0 +#define QSPI_FUN_SET_DELAY_MASK (0xFFFF << QSPI_FUN_SET_DELAY_SHIFT) + +#define QSPI_WIP_W_CMD_SHIFT 24 +#define QSPI_WIP_W_CMD_MASK (0xFF << QSPI_WIP_W_CMD_SHIFT) +#define QSPI_WIP_W_TRANSFER_SHIFT 3 +#define QSPI_WIP_W_TRANSFER_MASK (0x3 << QSPI_WIP_W_TRANSFER_SHIFT) +#define QSPI_WIP_W_SCK_SEL_SHIFT 0 +#define QSPI_WIP_W_SCK_SEL_MASK (0x7 << QSPI_WIP_W_SCK_SEL_SHIFT) + +#define QSPI_WP_EN_SHIFT 17 +#define QSPI_WP_EN_MASK (0x1 << QSPI_WP_EN_SHIFT) +#define QSPI_WP_IO2_SHIFT 16 +#define QSPI_WP_IO2_MASK (0x1 << QSPI_WP_IO2_SHIFT) +#define QSPI_WP_HOLD_SHIFT 8 +#define QSPI_WP_HOLD_MASK (0xFF << QSPI_WP_HOLD_SHIFT) +#define QSPI_WP_SETUP_SHIFT 0 +#define QSPI_WP_SETUP_MASK (0xFF << QSPI_WP_SETUP_SHIFT) + +#define QSPI_MODE_VALID_SHIFT 8 +#define QSPI_MODE_VALID_MASK (0xFF << QSPI_MODE_VALID_SHIFT) +#define QSPI_MODE_SHIFT 0 +#define QSPI_MODE_MASK (0xFF << QSPI_MODE_SHIFT) + +#define FSIZE_VAL(size) (__fls(size) - 1) + +#define PHYTIUM_MAX_MMAP_S SZ_512M +#define PHYTIUM_MAX_NORCHIP 4 + +#define PHYTIUM_QSPI_FIFO_SZ 32 +#define PHYTIUM_QSPI_FIFO_TIMEOUT_US 50000 +#define PHYTIUM_QSPI_BUSY_TIMEOUT_US 100000 + +#define PHYTIUM_SCK_SEL 0x05 +#define PHYTIUM_CMD_SCK_SEL 0x07 + +#define PHYTIUM_FMODE_MM 0x01 +#define PHYTIUM_FMODE_IN 0x02 + +/* + * the codes of the different commands + */ +#define CMD_WRDI 0x04 +#define CMD_RDID 0x9F +#define CMD_RDSR 0x05 +#define CMD_WREN 0x06 +#define CMD_RDAR 0x65 +#define CMD_P4E 0x20 +#define CMD_4P4E 0x21 +#define CMD_BE 0x60 +#define CMD_4BE 0xC7 +#define CMD_READ 0x03 +#define CMD_FAST_READ 0x0B +#define CMD_QOR 0x6B +#define CMD_QIOR 0xEB +#define CMD_DDRFR 0x0D +#define CMD_DDRQIOQ 0xED +#define CMD_PP 0x02 +#define CMD_QPP 0x32 +#define CMD_SE 0xD8 +#define CMD_4FAST_READ 0x0C +#define CMD_4READ 0x13 +#define CMD_4QOR 0x6C +#define CMD_4QIOR 0xEC +#define CMD_4DDRFR 0x0E +#define CMD_4DDRQIOR 0xEE +#define CMD_4PP 0x12 +#define CMD_4QPP 0x34 +#define CMD_4SE 0xDC + +#define PHYTIUM_QSPI_1_1_1 0 +#define PHYTIUM_QSPI_1_1_2 1 +#define PHYTIUM_QSPI_1_1_4 2 +#define PHYTIUM_QSPI_1_2_2 3 +#define PHYTIUM_QSPI_1_4_4 4 +#define PHYTIUM_QSPI_2_2_2 5 +#define PHYTIUM_QSPI_4_4_4 6 + +struct phytium_qspi_flash { + struct spi_nor nor; + struct phytium_qspi *qspi; + u32 cs; + u32 fsize; + u32 presc; + u32 clk_div; + u32 read_mode; + bool registered; + u32 prefetch_limit; + u32 addr_width; + u32 read_cmd; +}; + +struct phytium_qspi { + struct device *dev; + void __iomem *io_base; + void __iomem *mm_base; + resource_size_t mm_size; + u32 nor_num; + struct clk *clk; + u32 clk_rate; + struct phytium_qspi_flash flash[PHYTIUM_MAX_NORCHIP]; + + spinlock_t spinlock; + + /* + * to protect device configuration, could be different between + * 2 flash access (bk1, bk2) + */ + struct mutex lock; +}; + +/* Need to enable p_buffer */ +static int memcpy_from_ftreg(struct phytium_qspi *qspi, u_char *buf, size_t len) +{ + int i; + u32 val = 0; + + if (!qspi || !buf) + return -EINVAL; + + for (i = 0; i < len; i++) { + if (0 == i % 4) + val = readl_relaxed(qspi->io_base + QSPI_LD_PORT_REG); + + buf[i] = (u_char) (val >> (i % 4) * 8) & 0xFF; + } + + return 0; +} + +/* Not to enable p_buffer */ +static int memcpy_to_ftreg(struct phytium_qspi *qspi, const u8 *buf, size_t len) +{ + u32 val = 0; + + if (!qspi || !buf || (len >= 8)) + return -EINVAL; + + if (1 == len) { + val = buf[0]; + } else if (2 == len) { + val = buf[1]; + val = (val << 8) + buf[0]; + } else if (3 == len) { + val = buf[2]; + val = (val << 8) + buf[1]; + val = (val << 8) + buf[0]; + } else if (4 == len) { + val = buf[3]; + val = (val << 8) + buf[2]; + val = (val << 8) + buf[1]; + val = (val << 8) + buf[0]; + } + + writel_relaxed(val, qspi->io_base + QSPI_LD_PORT_REG); + + return 0; +} + +static int phytium_qspi_wait_cmd(struct phytium_qspi *qspi, + struct phytium_qspi_flash *flash) +{ + u32 cmd = 0; + u32 cnt = 0; + + cmd |= CMD_RDSR << QSPI_CMD_PORT_CMD_SHIFT; + cmd |= BIT(QSPI_CMD_PORT_DATA_TRANSFER_SHIFT); + cmd |= flash->cs << QSPI_CMD_PORT_CS_SHIFT; + + writel_relaxed(cmd, qspi->io_base + QSPI_CMD_PORT_REG); + + cnt = PHYTIUM_QSPI_BUSY_TIMEOUT_US / 10; + while (readl_relaxed(qspi->io_base + QSPI_LD_PORT_REG) & 0x01) { + udelay(10); + cnt--; + if (!cnt) { + dev_err(qspi->dev, "wait command process timeout\n"); + break; + } + } + + return !cnt; +} + +static int phytium_qspi_cmd_enable(struct phytium_qspi *qspi) +{ + u32 val = 0; + + writel_relaxed(val, qspi->io_base + QSPI_LD_PORT_REG); + + return 0; +} + +static int phytium_qspi_write_enable(struct phytium_qspi *qspi, + struct phytium_qspi_flash *flash) +{ + u32 cmd = 0; + + cmd = CMD_WREN << QSPI_CMD_PORT_CMD_SHIFT; + cmd |= PHYTIUM_SCK_SEL << QSPI_CMD_PORT_SCK_SEL_SHIFT; + cmd |= flash->cs << QSPI_CMD_PORT_CS_SHIFT; + + writel_relaxed(cmd, qspi->io_base + QSPI_CMD_PORT_REG); + phytium_qspi_cmd_enable(qspi); + + return 0; +} + +static int phytium_qspi_write_disable(struct phytium_qspi *qspi, + struct phytium_qspi_flash *flash) +{ + u32 cmd = 0; + + cmd = CMD_WRDI << QSPI_CMD_PORT_CMD_SHIFT; + cmd |= PHYTIUM_SCK_SEL << QSPI_CMD_PORT_SCK_SEL_SHIFT; + cmd |= flash->cs << QSPI_CMD_PORT_CS_SHIFT; + + writel_relaxed(cmd, qspi->io_base + QSPI_CMD_PORT_REG); + phytium_qspi_cmd_enable(qspi); + + return 0; +} + +static int phytium_qspi_read_flash_id(struct phytium_qspi *qspi, + struct phytium_qspi_flash *flash, u8 opcode, u8 *buf, int len) +{ + u32 cmd = 0; + unsigned long iflags; + + cmd = opcode << QSPI_CMD_PORT_CMD_SHIFT; + cmd |= BIT(QSPI_CMD_PORT_DATA_TRANSFER_SHIFT); + cmd |= BIT(QSPI_CMD_PORT_P_BUFFER_SHIFT); + cmd |= PHYTIUM_CMD_SCK_SEL << QSPI_CMD_PORT_SCK_SEL_SHIFT; + cmd |= flash->cs << QSPI_CMD_PORT_CS_SHIFT; + + writel_relaxed(cmd, qspi->io_base + QSPI_CMD_PORT_REG); + phytium_qspi_cmd_enable(qspi); + + spin_lock_irqsave(&qspi->spinlock, iflags); + memcpy_from_ftreg(qspi, buf, len); + spin_unlock_irqrestore(&qspi->spinlock, iflags); + + dev_dbg(qspi->dev, "read flash id:%x\n", *(u32 *)buf); + return 0; +} + +static int phytium_qspi_read_flash_sfdp(struct phytium_qspi *qspi, + struct phytium_qspi_flash *flash, struct spi_nor *nor, loff_t from, u8 *buf, int len) +{ + unsigned long iflags; + u32 cmd = 0; + u8 opcode = nor->read_opcode; + + cmd = opcode << QSPI_CMD_PORT_CMD_SHIFT; + cmd |= BIT(QSPI_CMD_PORT_DATA_TRANSFER_SHIFT); + cmd |= BIT(QSPI_CMD_PORT_P_BUFFER_SHIFT); + cmd |= BIT(QSPI_CMD_PORT_CMD_ADDR_SHIFT); + cmd |= PHYTIUM_SCK_SEL << QSPI_CMD_PORT_SCK_SEL_SHIFT; + cmd |= flash->cs << QSPI_CMD_PORT_CS_SHIFT; + cmd |= BIT(QSPI_CMD_PORT_LATENCY_SHIFT); + cmd |= QSPI_CMD_PORT_DUMMY(nor->read_dummy - 1); + + writel_relaxed(cmd, qspi->io_base + QSPI_CMD_PORT_REG); + writel_relaxed(from, qspi->io_base + QSPI_ADDR_PORT_REG); + phytium_qspi_cmd_enable(qspi); + + spin_lock_irqsave(&qspi->spinlock, iflags); + memcpy_from_ftreg(qspi, buf, len); + spin_unlock_irqrestore(&qspi->spinlock, iflags); + + dev_dbg(qspi->dev, "read flash sfdp:0x%llx 0x%llx\n", + *(u64 *)buf, *(u64 *)(buf + 8)); + return len; +} + +static int phytium_qspi_read_flash_sr1(struct phytium_qspi *qspi, + struct phytium_qspi_flash *flash, u8 opcode, u8 *buf, int len) +{ + u32 cmd = 0; + u32 val; + + cmd = opcode << QSPI_CMD_PORT_CMD_SHIFT; + cmd |= BIT(QSPI_CMD_PORT_DATA_TRANSFER_SHIFT); + cmd |= (len << QSPI_CMD_PORT_RW_NUM_SHIFT) & QSPI_CMD_PORT_RW_NUM_MASK; + cmd |= PHYTIUM_CMD_SCK_SEL << QSPI_CMD_PORT_SCK_SEL_SHIFT; + cmd |= flash->cs << QSPI_CMD_PORT_CS_SHIFT; + + writel_relaxed(cmd, qspi->io_base + QSPI_CMD_PORT_REG); + phytium_qspi_cmd_enable(qspi); + + val = readl_relaxed(qspi->io_base + QSPI_LD_PORT_REG); + buf[0] = (u8)val; + + return 0; +} + +static int phytium_qspi_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, size_t len) +{ + struct phytium_qspi_flash *flash = nor->priv; + struct device *dev = flash->qspi->dev; + struct phytium_qspi *qspi = flash->qspi; + unsigned long iflags; + u32 cmd = 0; + + dev_dbg(dev, "read_reg: cmd:%#.2x buf:%pK len:%#lx\n", opcode, buf, len); + + switch (opcode) { + case CMD_RDID: + phytium_qspi_read_flash_id(qspi, flash, opcode, buf, len); + return 0; + case CMD_RDSR: + phytium_qspi_read_flash_sr1(qspi, flash, opcode, buf, len); + return 0; + default: + break; + } + + cmd = opcode << QSPI_CMD_PORT_CMD_SHIFT; + cmd |= BIT(QSPI_CMD_PORT_DATA_TRANSFER_SHIFT); + cmd |= BIT(QSPI_CMD_PORT_P_BUFFER_SHIFT); + cmd |= PHYTIUM_CMD_SCK_SEL << QSPI_CMD_PORT_SCK_SEL_SHIFT; + cmd |= flash->cs << QSPI_CMD_PORT_CS_SHIFT; + + writel_relaxed(cmd, qspi->io_base + QSPI_CMD_PORT_REG); + phytium_qspi_cmd_enable(qspi); + + spin_lock_irqsave(&qspi->spinlock, iflags); + memcpy_from_ftreg(qspi, buf, len); + spin_unlock_irqrestore(&qspi->spinlock, iflags); + + return 0; +} + +static int phytium_qspi_write_reg(struct spi_nor *nor, u8 opcode, const u8 *buf, size_t len) +{ + struct phytium_qspi_flash *flash = nor->priv; + struct device *dev = flash->qspi->dev; + struct phytium_qspi *qspi = flash->qspi; + u32 cmd = 0; + + dev_dbg(dev, "write_reg: cmd:%#.2x buf:%pK len:%#lx\n", opcode, buf, len); + + switch (opcode) { + case CMD_WREN: + phytium_qspi_write_enable(qspi, flash); + return 0; + case CMD_WRDI: + phytium_qspi_write_disable(qspi, flash); + return 0; + default: + break; + } + + cmd = opcode << QSPI_CMD_PORT_CMD_SHIFT; + cmd |= PHYTIUM_CMD_SCK_SEL << QSPI_CMD_PORT_SCK_SEL_SHIFT; + cmd |= flash->cs << QSPI_CMD_PORT_CS_SHIFT; + + if ((len > 8) || (NULL == buf)) { + dev_err(dev, "data length exceed. commad %x, len:%ld \n", opcode, len); + return -EINVAL; + } else if (len > 0) { + cmd |= ((len - 1) << QSPI_CMD_PORT_RW_NUM_SHIFT) & QSPI_CMD_PORT_RW_NUM_MASK; + cmd |= BIT(QSPI_CMD_PORT_DATA_TRANSFER_SHIFT); + } + + writel_relaxed(cmd, qspi->io_base + QSPI_CMD_PORT_REG); + memcpy_to_ftreg(qspi, buf, len); + + return 0; +} + +static ssize_t phytium_qspi_read_tmp(struct phytium_qspi *qspi, u32 read_cmd, loff_t from, size_t len, u_char *buf) +{ + u32 addr = (u32)from; + u64 val = 0; + + if (!qspi) + return -1; + + dev_dbg(qspi->dev, "read cmd:%x, addr:%x len:%zx\n", read_cmd, addr, len); + writel_relaxed(read_cmd, qspi->io_base + QSPI_RD_CFG_REG); + + memcpy_fromio(buf, qspi->mm_base + addr, len); + + val = *(u64 *)(buf); + dev_dbg(qspi->dev, "read val:%llx\n", val); + + return len; +} + +static ssize_t phytium_qspi_read(struct spi_nor *nor, loff_t from, size_t len, u8 *buf) +{ + struct phytium_qspi_flash *flash = nor->priv; + struct phytium_qspi *qspi = flash->qspi; + u32 cmd = nor->read_opcode; + u32 addr = (u32)from; + + addr = addr + flash->cs * flash->fsize; + + dev_dbg(qspi->dev, "read(%#.2x): buf:%pK from:%#.8x len:%#zx\n", nor->read_opcode, buf, addr, len); + + cmd = cmd << QSPI_RD_CFG_RD_CMD_SHIFT; + cmd |= BIT(QSPI_RD_CFG_D_BUFFER_SHIFT); + cmd |= flash->clk_div << QSPI_CMD_PORT_SCK_SEL_SHIFT; + + cmd &= ~QSPI_RD_CFG_RD_TRANSFER_MASK; + cmd |= (flash->addr_width << QSPI_RD_CFG_RD_TRANSFER_SHIFT); + + switch (nor->read_opcode) { + case CMD_READ: + case CMD_FAST_READ: + case CMD_QIOR: + case CMD_QOR: + cmd &= ~QSPI_RD_CFG_RD_ADDR_SEL_MASK; + break; + case CMD_4READ: + case CMD_4FAST_READ: + case CMD_4QOR: + case CMD_4QIOR: + cmd |= BIT(QSPI_RD_CFG_RD_ADDR_SEL_SHIFT); + break; + case 0x5A: + cmd &= ~QSPI_RD_CFG_RD_ADDR_SEL_MASK; + return phytium_qspi_read_flash_sfdp(qspi, flash, nor, from, buf, len); + break; + default: + break; + } + + if ((PHYTIUM_QSPI_1_1_4 == flash->addr_width) || (PHYTIUM_QSPI_1_4_4 == flash->addr_width)) { + cmd |= BIT(QSPI_RD_CFG_RD_LATENCY_SHIFT); + cmd &= ~QSPI_RD_CFG_DUMMY_MASK; + cmd |= (0x07 << QSPI_RD_CFG_DUMMY_SHIFT); + } + + dev_dbg(qspi->dev, "read(%#.2x): cmd:%#x\n", nor->read_opcode, cmd); + + if (cmd != flash->read_cmd) + flash->read_cmd = cmd; + + writel_relaxed(cmd, qspi->io_base + QSPI_RD_CFG_REG); + + memcpy_fromio(buf, qspi->mm_base + addr, len); + + return len; +} + +static ssize_t phytium_qspi_write(struct spi_nor *nor, loff_t to, size_t len, const u8 *buf) +{ + struct phytium_qspi_flash *flash = nor->priv; + struct device *dev = flash->qspi->dev; + struct phytium_qspi *qspi = flash->qspi; + u32 cmd = nor->program_opcode; + u32 addr = (u32)to; + int i; + u_char tmp[8] = {0}; + size_t mask = 0x03; + + addr = addr + flash->cs * flash->fsize; + + dev_dbg(dev, "write(%#.2x): buf:%p to:%#.8x len:%#zx\n", nor->program_opcode, buf, addr, len); + + if (addr & 0x03) { + dev_err(dev, "Addr not four-byte aligned!\n"); + return -EINVAL; + } + + cmd = cmd << QSPI_WR_CFG_WR_CMD_SHIFT; + cmd |= BIT(QSPI_WR_CFG_WR_MODE_SHIFT); + cmd |= PHYTIUM_CMD_SCK_SEL << QSPI_CMD_PORT_SCK_SEL_SHIFT; + + switch (nor->program_opcode) { + case CMD_PP: + case CMD_QPP: + cmd &= ~QSPI_WR_CFG_WR_ADDR_SEL_MASK; + break; + case CMD_4PP: + case CMD_4QPP: + cmd |= BIT(QSPI_WR_CFG_WR_ADDR_SEL_SHIFT); + break; + default: + dev_err(qspi->dev, "Not support program command:%#x\n", nor->erase_opcode); + return -EINVAL; + } + + dev_dbg(qspi->dev, "write cmd:%x\n", cmd); + + writel_relaxed(cmd, qspi->io_base + QSPI_WR_CFG_REG); + + for (i = 0; i < len/4; i++) + writel_relaxed(*(u32 *)(buf + 4*i), qspi->mm_base + addr + 4*i); + + if (len & mask) { + addr = addr + (len & ~mask); + phytium_qspi_read_tmp(qspi, flash->read_cmd, addr, 4, &tmp[0]); + memcpy(tmp, buf + (len & ~mask), len & mask); + writel_relaxed(*(u32 *)(tmp), qspi->mm_base + addr); + } + + writel_relaxed(QSPI_FLUSH_EN, qspi->io_base + QSPI_FLUSH_REG); + + phytium_qspi_wait_cmd(qspi, flash); + + return len; +} + +static int phytium_qspi_erase(struct spi_nor *nor, loff_t offs) +{ + struct phytium_qspi_flash *flash = nor->priv; + struct device *dev = flash->qspi->dev; + struct phytium_qspi *qspi = flash->qspi; + u32 cmd = nor->erase_opcode; + u32 addr = (u32)offs; + + dev_dbg(dev, "erase(%#.2x):offs:%#x\n", nor->erase_opcode, (u32)offs); + + phytium_qspi_write_enable(qspi, flash); + + cmd = cmd << QSPI_CMD_PORT_CMD_SHIFT; + cmd |= PHYTIUM_SCK_SEL << QSPI_CMD_PORT_SCK_SEL_SHIFT; + cmd |= flash->cs << QSPI_CMD_PORT_CS_SHIFT; + + /* s25fl256s1 not supoort D8, DC, 20, 21 */ + switch (nor->erase_opcode) { + case CMD_SE: + cmd &= ~QSPI_CMD_PORT_SEL_MASK; + cmd |= BIT(QSPI_CMD_PORT_CMD_ADDR_SHIFT); + writel_relaxed(addr, qspi->io_base + QSPI_ADDR_PORT_REG); + break; + case CMD_4SE: + cmd |= BIT(QSPI_CMD_PORT_SEL_SHIFT); + cmd |= BIT(QSPI_CMD_PORT_CMD_ADDR_SHIFT); + writel_relaxed(addr, qspi->io_base + QSPI_ADDR_PORT_REG); + break; + case CMD_P4E: + cmd &= ~QSPI_CMD_PORT_SEL_MASK; + cmd |= BIT(QSPI_CMD_PORT_CMD_ADDR_SHIFT); + writel_relaxed(addr, qspi->io_base + QSPI_ADDR_PORT_REG); + break; + case CMD_4P4E: + cmd |= BIT(QSPI_CMD_PORT_SEL_SHIFT); + cmd |= BIT(QSPI_CMD_PORT_CMD_ADDR_SHIFT); + writel_relaxed(addr, qspi->io_base + QSPI_ADDR_PORT_REG); + break; + case CMD_BE: + cmd &= ~QSPI_CMD_PORT_SEL_MASK; + break; + case CMD_4BE: + cmd |= BIT(QSPI_CMD_PORT_SEL_SHIFT); + break; + default: + dev_err(qspi->dev, "Not support erase command:%#x\n", nor->erase_opcode); + return -EINVAL; + } + + writel_relaxed(cmd, qspi->io_base + QSPI_CMD_PORT_REG); + phytium_qspi_cmd_enable(qspi); + phytium_qspi_wait_cmd(qspi, flash); + + return 0; +} + +static int phytium_qspi_prep(struct spi_nor *nor) +{ + struct phytium_qspi_flash *flash = nor->priv; + struct phytium_qspi *qspi = flash->qspi; + + mutex_lock(&qspi->lock); + return 0; +} + +static void phytium_qspi_unprep(struct spi_nor *nor) +{ + struct phytium_qspi_flash *flash = nor->priv; + struct phytium_qspi *qspi = flash->qspi; + + mutex_unlock(&qspi->lock); +} + +static int phytium_qspi_get_flash_size(struct phytium_qspi *qspi, u32 size) +{ + int ret = 0; + u32 value; + + switch (size) { + case SZ_4M: + value = 0; + break; + case SZ_8M: + value = 1; + break; + case SZ_16M: + value = 2; + break; + case SZ_32M: + value = 3; + break; + case SZ_64M: + value = 4; + break; + case SZ_128M: + value = 5; + break; + case SZ_256M: + value = 6; + break; + case SZ_512M: + value = 7; + break; + default: + value = 0; + ret = -EINVAL; + return ret; + } + + return value; +} + +static const struct spi_nor_controller_ops phytium_controller_ops = { + .prepare = phytium_qspi_prep, + .unprepare = phytium_qspi_unprep, + .read_reg = phytium_qspi_read_reg, + .write_reg = phytium_qspi_write_reg, + .read = phytium_qspi_read, + .write = phytium_qspi_write, + .erase = phytium_qspi_erase, +}; + +static int phytium_qspi_flash_setup(struct phytium_qspi *qspi, struct fwnode_handle *np) +{ + struct spi_nor_hwcaps hwcaps = { .mask = SNOR_HWCAPS_READ | SNOR_HWCAPS_READ_FAST | SNOR_HWCAPS_PP, }; + u32 width, presc; + u32 cs_num = 0; + u32 max_rate = 0; + u32 clk_div = 0; + u32 flash_cap = 0; + u32 addr_width = PHYTIUM_QSPI_1_1_1; + struct phytium_qspi_flash *flash; + struct mtd_info *mtd; + int ret; + + fwnode_property_read_u32(np, "reg", &cs_num); + if (cs_num >= PHYTIUM_MAX_NORCHIP) + return -EINVAL; + + fwnode_property_read_u32(np, "spi-max-frequency", &max_rate); + if (!max_rate) + return -EINVAL; + + fwnode_property_read_u32(np, "spi-clk-div", &clk_div); + if (!clk_div) + clk_div = PHYTIUM_SCK_SEL; + + if (clk_div < 4) + return -EINVAL; + + presc = DIV_ROUND_UP(qspi->clk_rate, max_rate) - 1; + + fwnode_property_read_u32(np, "spi-rx-bus-width", &width); + if (!width) + width = 1; + + if (width == 4) { + hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4; + addr_width = PHYTIUM_QSPI_1_1_4; + } else if (width == 2) { + hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2; + addr_width = PHYTIUM_QSPI_1_1_2; + } else if (width != 1) + return -EINVAL; + + flash = &qspi->flash[cs_num]; + + flash->qspi = qspi; + flash->cs = cs_num; + flash->presc = presc; + flash->clk_div = clk_div; + flash->addr_width = addr_width; + flash->nor.dev = qspi->dev; + if (qspi->dev->of_node) + spi_nor_set_flash_node(&flash->nor, qspi->dev->of_node); + flash->nor.priv = flash; + flash->nor.controller_ops = &phytium_controller_ops; + + ret = spi_nor_scan(&flash->nor, NULL, &hwcaps); + if (ret) { + dev_err(qspi->dev, "device scan failed\n"); + return ret; + } + + mtd = &flash->nor.mtd; + + flash->fsize = mtd->size; + flash->prefetch_limit = mtd->size - PHYTIUM_QSPI_FIFO_SZ; + + ret = phytium_qspi_get_flash_size(flash->qspi, mtd->size); + if (ret < 0) { + dev_err(qspi->dev, "flash size invalid\n"); + return ret; + } + + flash_cap = cs_num << QSPI_FLASH_CAP_NUM_SHIFT; + flash_cap |= ret; + writel_relaxed(flash_cap, qspi->io_base + QSPI_FLASH_CAP_REG); + + flash->read_mode = PHYTIUM_FMODE_MM; + + ret = mtd_device_register(mtd, NULL, 0); + if (ret) { + dev_err(qspi->dev, "mtd device parse failed\n"); + return ret; + } + + flash->registered = true; + + dev_dbg(qspi->dev, "read mm:%s %px cs:%d bus:%d clk-div:%d\n", + flash->read_mode == PHYTIUM_FMODE_MM ? "yes" : "no", + qspi->mm_base, cs_num, width, clk_div); + + dev_dbg(qspi->dev, "mtd->size:%llx, mtd->erasesize:%x, fsize:%x\n", + mtd->size, mtd->erasesize, flash->fsize); + + return 0; +} + +static void phytium_qspi_mtd_free(struct phytium_qspi *qspi) +{ + int i; + + for (i = 0; i < PHYTIUM_MAX_NORCHIP; i++) + if (qspi->flash[i].registered) + mtd_device_unregister(&qspi->flash[i].nor.mtd); +} + +static ssize_t clk_div_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct phytium_qspi *qspi = dev_get_drvdata(dev); + struct phytium_qspi_flash *flash = &qspi->flash[0]; + + return sprintf(buf, "Flash 0 clk-div: %d\n", flash->clk_div); +} + +static ssize_t clk_div_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t size) +{ + struct phytium_qspi *qspi = dev_get_drvdata(dev); + struct phytium_qspi_flash *flash = &qspi->flash[0]; + long value; + char *token; + ssize_t status; + + token = strsep ((char **)&buf, " "); + if (!token) + return -EINVAL; + + status = kstrtol(token, 0, &value); + if (status) + return status; + + flash->clk_div = (u8)value; + + return size; +} +static DEVICE_ATTR_RW(clk_div); + +static struct attribute *phytium_qspi_attrs[] = { + &dev_attr_clk_div.attr, + NULL, +}; + +static struct attribute_group phytium_qspi_attr_group = { + .attrs = phytium_qspi_attrs, +}; + +static int phytium_qspi_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct fwnode_handle *flash_np; + struct phytium_qspi *qspi; + struct resource *res; + int ret; + + qspi = devm_kzalloc(dev, sizeof(*qspi), GFP_KERNEL); + if (!qspi) + return -ENOMEM; + + qspi->nor_num = device_get_child_node_count(dev); + if (!qspi->nor_num || qspi->nor_num > PHYTIUM_MAX_NORCHIP) + return -ENODEV; + + if (dev->of_node) + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qspi"); + else if (has_acpi_companion(dev)) { + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + } + qspi->io_base = devm_ioremap_resource(dev, res); + if (IS_ERR(qspi->io_base)) + return PTR_ERR(qspi->io_base); + + + if (dev->of_node) + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qspi_mm"); + else if (has_acpi_companion(dev)) + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + + qspi->mm_base = devm_ioremap_resource(dev, res); + if (IS_ERR(qspi->mm_base)) + return PTR_ERR(qspi->mm_base); + + qspi->mm_size = resource_size(res); + + if (dev->of_node) { + qspi->clk = devm_clk_get(dev, NULL); + if (IS_ERR(qspi->clk)) + return PTR_ERR(qspi->clk); + + qspi->clk_rate = clk_get_rate(qspi->clk); + if (!qspi->clk_rate) + return -EINVAL; + + ret = clk_prepare_enable(qspi->clk); + if (ret) { + dev_err(dev, "can not enable the clock\n"); + return ret; + } + } + else if (has_acpi_companion(dev)) { /* ACPI table not pass clk rate */ + qspi->clk_rate = 50000000; + } + + qspi->dev = dev; + platform_set_drvdata(pdev, qspi); + mutex_init(&qspi->lock); + spin_lock_init(&qspi->spinlock); + + fwnode_for_each_available_child_node(dev_fwnode(dev), flash_np) { + ret = phytium_qspi_flash_setup(qspi, flash_np); + if (ret) { + dev_err(dev, "unable to setup flash chip\n"); + goto err_flash; + } + } + + ret = sysfs_create_group(&qspi->dev->kobj, &phytium_qspi_attr_group); + if (ret) { + dev_err(dev, "unable to create sysfs\n"); + goto err_flash; + } + + return 0; + +err_flash: + mutex_destroy(&qspi->lock); + phytium_qspi_mtd_free(qspi); + + clk_disable_unprepare(qspi->clk); + return ret; +} + +static int phytium_qspi_remove(struct platform_device *pdev) +{ + struct phytium_qspi *qspi = platform_get_drvdata(pdev); + + sysfs_remove_group(&qspi->dev->kobj, &phytium_qspi_attr_group); + + phytium_qspi_mtd_free(qspi); + mutex_destroy(&qspi->lock); + + clk_disable_unprepare(qspi->clk); + return 0; +} + +#ifdef CONFIG_ACPI +static const struct acpi_device_id phytium_qspi_acpi_ids[] = { + { "PHYT0011", 0 }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(acpi, phytium_qspi_acpi_ids); +#endif + +static const struct of_device_id phytium_qspi_match[] = { + { .compatible = "phytium,qspi" }, + { } +}; +MODULE_DEVICE_TABLE(of, phytium_qspi_match); + +static struct platform_driver phytium_qspi_driver = { + .probe = phytium_qspi_probe, + .remove = phytium_qspi_remove, + .driver = { + .name = "phytium-quadspi", + .of_match_table = phytium_qspi_match, + .acpi_match_table = ACPI_PTR(phytium_qspi_acpi_ids), + }, +}; +module_platform_driver(phytium_qspi_driver); + +MODULE_AUTHOR("Mingshuai Zhu "); +MODULE_AUTHOR("Shaojun Yang "); +MODULE_DESCRIPTION("Phytium QuadSPI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/target/linux/phytium/files-5.10/drivers/net/can/phytium/Kconfig b/target/linux/phytium/files-5.10/drivers/net/can/phytium/Kconfig new file mode 100644 index 000000000..a23216d23 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/net/can/phytium/Kconfig @@ -0,0 +1,34 @@ +# SPDX-License-Identifier: GPL-2.0-only +menuconfig CAN_PHYTIUM + tristate "Phytium CAN support" + help + Say Y here if you want support for Phytium CAN controller framework. + This is common support for devices that embed the Phytium CAN IP. + + To compile this driver as a module, choose M here: the module will + be called phytium_can. + +if CAN_PHYTIUM + +config CAN_PHYTIUM_PLATFORM + tristate "Phytium CAN support for io-mapped devices" + depends on HAS_IOMEM + help + Say Y here is you want to support for IO Mapped Phytium CAN controller. + This support is for devices that have the Phytium CAN controller IP + embedded into the device and the IP is IO Mapped to the processor. + + To compile this driver as a module, choose M here: the module will + be called phytium_can_platform. + +config CAN_PHYTIUM_PCI + tristate "Phytium CAN support for PCI devices" + depends on PCI + help + Say Y here is you want to support for Phytium CAN controller connected + to the PCI bus. This support is for devices that have the Phytium CAN + controller IP embedded into a PCI device. + + To compile this driver as a module, choose M here: the module will + be called phytium_can_pci. +endif diff --git a/target/linux/phytium/files-5.10/drivers/net/can/phytium/Makefile b/target/linux/phytium/files-5.10/drivers/net/can/phytium/Makefile new file mode 100644 index 000000000..7ef554fca --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/net/can/phytium/Makefile @@ -0,0 +1,8 @@ +# +# Makefile for the Phytium CAN controller drivers. +# +# + +obj-$(CONFIG_CAN_PHYTIUM) += phytium_can.o +obj-$(CONFIG_CAN_PHYTIUM_PLATFORM) += phytium_can_platform.o +obj-$(CONFIG_CAN_PHYTIUM_PCI) += phytium_can_pci.o diff --git a/target/linux/phytium/files-5.10/drivers/net/can/phytium/phytium_can.c b/target/linux/phytium/files-5.10/drivers/net/can/phytium/phytium_can.c new file mode 100644 index 000000000..95ec7311b --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/net/can/phytium/phytium_can.c @@ -0,0 +1,1156 @@ +// SPDX-License-Identifier: GPL-2.0 +/* CAN bus driver for Phytium CAN controller + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#include + +#include "phytium_can.h" + +/* register definition */ +enum phytium_can_reg { + CAN_CTRL = 0x00, /* Global control register */ + CAN_INTR = 0x04, /* Interrupt register */ + CAN_ARB_RATE_CTRL = 0x08, /* Arbitration rate control register */ + CAN_DAT_RATE_CTRL = 0x0c, /* Data rate control register */ + CAN_ACC_ID0 = 0x10, /* Acceptance identifier0 register */ + CAN_ACC_ID1 = 0x14, /* Acceptance identifier1 register */ + CAN_ACC_ID2 = 0x18, /* Acceptance identifier2 register */ + CAN_ACC_ID3 = 0x1c, /* Acceptance identifier3 register */ + CAN_ACC_ID0_MASK = 0x20, /* Acceptance identifier0 mask register */ + CAN_ACC_ID1_MASK = 0x24, /* Acceptance identifier1 mask register */ + CAN_ACC_ID2_MASK = 0x28, /* Acceptance identifier2 mask register */ + CAN_ACC_ID3_MASK = 0x2c, /* Acceptance identifier3 mask register */ + CAN_XFER_STS = 0x30, /* Transfer status register */ + CAN_ERR_CNT = 0x34, /* Error counter register */ + CAN_FIFO_CNT = 0x38, /* FIFO counter register */ + CAN_DMA_CTRL = 0x3c, /* DMA request control register */ + CAN_XFER_EN = 0x40, /* Transfer enable register */ + CAN_INTR1 = 0x44, /* Interrupt register 1 */ + CAN_FRM_INFO = 0x48, /* Frame valid number register */ + CAN_TIME_OUT = 0x4c, /* Timeout register */ + CAN_TIME_OUT_CNT = 0x50, /* Timeout counter register */ + CAN_INTR2 = 0x54, /* Interrupt register 2 */ + CAN_TX_FIFO = 0x100, /* TX FIFO shadow register */ + CAN_RX_FIFO = 0x200, /* RX FIFO shadow register */ + CAN_RX_INFO_FIFO = 0x300, /* RX information FIFO shadow register */ + CAN_PIDR4 = 0xfd0, /* Peripheral Identification Register 4 */ + CAN_PIDR0 = 0xfe0, /* Peripheral Identification Register 0 */ + CAN_PIDR1 = 0xfe4, /* Peripheral Identification Register 1 */ + CAN_PIDR2 = 0xfe8, /* Peripheral Identification Register 2 */ + CAN_PIDR3 = 0xfec, /* Peripheral Identification Register 3 */ + CAN_CIDR0 = 0xff0, /* Component Identification Register 0 */ + CAN_CIDR1 = 0xff4, /* Component Identification Register 1 */ + CAN_CIDR2 = 0xff8, /* Component Identification Register 2 */ + CAN_CIDR3 = 0xffc, /* Component Identification Register 3 */ +}; + +/* Global control register (CTRL) */ +#define CTRL_XFER BIT(0) /* Transfer enable */ +#define CTRL_TXREQ BIT(1) /* Transmit request */ +#define CTRL_AIME BIT(2) /* Acceptance identifier mask enable */ +#define CTRL_TTS BIT(3) /* Transmit trigger strategy */ +#define CTRL_RST BIT(7) /* Write 1 to soft reset and self clear */ +#define CTRL_RFEIDF BIT(8) /* Allow RX frame end interrupt during ID filtered frame */ +#define CTRL_RFEDT BIT(9) /* Allow RX frame end interrupt during TX frame */ +#define CTRL_IOF BIT(10) /* Ignore overload flag internally */ +#define CTRL_FDCRC BIT(11) /* CANFD CRC mode */ + +/* Interrupt register (INTR) */ +#define INTR_BOIS BIT(0) /* Bus off interrupt status */ +#define INTR_PWIS BIT(1) /* Passive warning interrupt status */ +#define INTR_PEIS BIT(2) /* Passive error interrupt status */ +#define INTR_RFIS BIT(3) /* RX FIFO full interrupt status */ +#define INTR_TFIS BIT(4) /* TX FIFO empty interrupt status */ +#define INTR_REIS BIT(5) /* RX frame end interrupt status */ +#define INTR_TEIS BIT(6) /* TX frame end interrupt status */ +#define INTR_EIS BIT(7) /* Error interrupt status */ +#define INTR_BOIE BIT(8) /* Bus off interrupt enable */ +#define INTR_PWIE BIT(9) /* Passive warning interrupt enable */ +#define INTR_PEIE BIT(10) /* Passive error interrupt enable */ +#define INTR_RFIE BIT(11) /* RX FIFO full interrupt enable */ +#define INTR_TFIE BIT(12) /* TX FIFO empty interrupt enable */ +#define INTR_REIE BIT(13) /* RX frame end interrupt enable */ +#define INTR_TEIE BIT(14) /* TX frame end interrupt enable */ +#define INTR_EIE BIT(15) /* Error interrupt enable */ +#define INTR_BOIC BIT(16) /* Bus off interrupt clear */ +#define INTR_PWIC BIT(17) /* Passive warning interrupt clear */ +#define INTR_PEIC BIT(18) /* Passive error interrupt clear */ +#define INTR_RFIC BIT(19) /* RX FIFO full interrupt clear */ +#define INTR_TFIC BIT(20) /* TX FIFO empty interrupt clear */ +#define INTR_REIC BIT(21) /* RX frame end interrupt clear */ +#define INTR_TEIC BIT(22) /* TX frame end interrupt clear */ +#define INTR_EIC BIT(23) /* Error interrupt clear */ + +#define INTR_STATUS_MASK (INTR_BOIS | INTR_PWIS | INTR_PEIS | INTR_RFIS | \ + INTR_TFIS | INTR_REIS | INTR_TEIS | INTR_EIS) +#define INTR_EN_MASK (INTR_BOIE | INTR_RFIE | INTR_REIE | INTR_TEIE | \ + INTR_EIE) +#define INTR_CLEAR_MASK (INTR_BOIC | INTR_PWIC | INTR_PEIC | INTR_RFIC | \ + INTR_TFIC | INTR_REIC | INTR_TEIC | INTR_EIC) + +/* Arbitration rate control register (ARB_RATE_CTRL) */ +#define ARB_RATE_CTRL_ARJW GENMASK(1, 0) /* Arbitration field resync jump width */ +#define ARB_RATE_CTRL_APRS GENMASK(4, 2) /* Arbitration field propagation segment */ +#define ARB_RATE_CTRL_APH1S GENMASK(7, 5) /* Arbitration field phase1 segment */ +#define ARB_RATE_CTRL_APH2S GENMASK(10, 8) /* Arbitration field phase2 segment */ +#define ARB_RATE_CTRL_APD GENMASK(28, 16) /* Arbitration field prescaler divider */ + +/* Data rate control register (DAT_RATE_CTRL) */ +#define DAT_RATE_CTRL_DRJW GENMASK(1, 0) /* Data field resync jump width */ +#define DAT_RATE_CTRL_DPRS GENMASK(4, 2) /* Data field propagation segment */ +#define DAT_RATE_CTRL_DPH1S GENMASK(7, 5) /* Data field phase1 segment */ +#define DAT_RATE_CTRL_DPH2S GENMASK(10, 8) /* Data field phase2 segment */ +#define DAT_RATE_CTRL_DPD GENMASK(28, 16) /* Data field prescaler divider */ + +/* Acceptance identifierX register (ACC_IDX) */ +#define ACC_IDX_AID_MASK GENMASK(28, 0) /* Acceptance identifier */ + +/* Acceptance identifier0 mask register (ACC_ID0_MASK) */ +#define ACC_IDX_MASK_AID_MASK GENMASK(28, 0) /* Acceptance identifier mask */ + +/* Transfer status register (XFER_STS) */ +#define XFER_STS_FRAS GENMASK(2, 0) /* Frame status */ +#define XFER_STS_FIES GENMASK(7, 3) /* Field status */ +#define XFER_STS_FIES_IDLE (0x0) /* idle */ +#define XFER_STS_FIES_ARBITRATION (0x1) /* arbitration */ +#define XFER_STS_FIES_TX_CTRL (0x2) /* transmit control */ +#define XFER_STS_FIES_TX_DATA (0x3) /* transmit data */ +#define XFER_STS_FIES_TX_CRC (0x4) /* transmit crc */ +#define XFER_STS_FIES_TX_FRM (0x5) /* transmit frame */ +#define XFER_STS_FIES_RX_CTRL (0x6) /* receive control */ +#define XFER_STS_FIES_RX_DATA (0x7) /* receive data */ +#define XFER_STS_FIES_RX_CRC (0x8) /* receive crc */ +#define XFER_STS_FIES_RX_FRM (0x9) /* receive frame */ +#define XFER_STS_FIES_INTERMISSION (0xa) /* intermission */ +#define XFER_STS_FIES_TX_SUSPD (0xb) /* transmit suspend */ +#define XFER_STS_FIES_BUS_IDLE (0xc) /* bus idle */ +#define XFER_STS_FIES_OVL_FLAG (0xd) /* overload flag */ +#define XFER_STS_FIES_OVL_DLM (0xe) /* overload delimiter */ +#define XFER_STS_FIES_ERR_FLAG (0xf) /* error flag */ +#define XFER_STS_FIES_ERR_DLM (0x10) /* error delimiter */ +#define XFER_STS_FIES_BUS_OFF (0x11) /* bus off */ +#define XFER_STS_TS BIT(8) /* Transmit status */ +#define XFER_STS_RS BIT(9) /* Receive status */ +#define XFER_STS_XFERS BIT(10) /* Transfer status */ + +/* Error counter register (ERR_CNT) */ +#define ERR_CNT_REC GENMASK(8, 0) /* Receive error counter */ +#define ERR_CNT_TEC GENMASK(24, 16) /* Transmit error counter */ + +/* FIFO counter register (FIFO_CNT) */ +#define FIFO_CNT_RFN GENMASK(6, 0) /* Receive FIFO valid data number */ +#define FIFO_CNT_TFN GENMASK(22, 16) /* Transmit FIFO valid data number */ + +/* DMA request control register (DMA_CTRL) */ +#define DMA_CTRL_RFTH GENMASK(5, 0) /* Receive FIFO DMA request threshold */ +#define DMA_CTRL_RFRE BIT(6) /* Receive FIFO DMA request enable */ +#define DMA_CTRL_TFTH GENMASK(21, 16) /* Transmit FIFO DMA request threshold */ +#define DMA_CTRL_TFRE BIT(22) /* Transmit FIFO DMA request enable */ + +/* Transfer enable register (XFER_EN) */ +#define XFER_EN_XFER BIT(0) /* Transfer enable */ + +/* Interrupt register 1 (INTR1) */ +#define INTR1_RF1IS BIT(0) /* RX FIFO 1/4 interrupt status */ +#define INTR1_RF2IS BIT(1) /* RX FIFO 1/2 interrupt status */ +#define INTR1_RF3IS BIT(2) /* RX FIFO 3/4 interrupt status */ +#define INTR1_RF4IS BIT(3) /* RX FIFO full interrupt status */ +#define INTR1_TF1IS BIT(4) /* TX FIFO 1/4 interrupt status */ +#define INTR1_TF2IS BIT(5) /* TX FIFO 1/2 interrupt status */ +#define INTR1_TF3IS BIT(6) /* TX FIFO 3/4 interrupt status */ +#define INTR1_TF4IS BIT(7) /* TX FIFO empty interrupt status */ +#define INTR1_RF1IE BIT(8) /* RX FIFO 1/4 interrupt enable */ +#define INTR1_RF2IE BIT(9) /* RX FIFO 1/2 interrupt enable */ +#define INTR1_RF3IE BIT(10) /* RX FIFO 3/4 interrupt enable */ +#define INTR1_RF4IE BIT(11) /* RX FIFO full interrupt enable */ +#define INTR1_TF1IE BIT(12) /* TX FIFO 1/4 interrupt enable */ +#define INTR1_TF2IE BIT(13) /* TX FIFO 1/2 interrupt enable */ +#define INTR1_TF3IE BIT(14) /* TX FIFO 3/4 interrupt enable */ +#define INTR1_TF4IE BIT(15) /* TX FIFO empty interrupt enable */ +#define INTR1_RF1IC BIT(16) /* RX FIFO 1/4 interrupt clear */ +#define INTR1_RF2IC BIT(17) /* RX FIFO 1/2 interrupt clear */ +#define INTR1_RF3IC BIT(18) /* RX FIFO 3/4 interrupt clear */ +#define INTR1_RF4IC BIT(19) /* RX FIFO full interrupt clear */ +#define INTR1_TF1IC BIT(20) /* TX FIFO 1/4 interrupt clear */ +#define INTR1_TF2IC BIT(21) /* TX FIFO 1/2 interrupt clear */ +#define INTR1_TF3IC BIT(22) /* TX FIFO 3/4 interrupt clear */ +#define INTR1_TF4IC BIT(23) /* TX FIFO empty interrupt clear */ +#define INTR1_RF1RIS BIT(24) /* RX FIFO 1/4 raw interrupt status */ +#define INTR1_RF2RIS BIT(25) /* RX FIFO 1/2 raw interrupt status */ +#define INTR1_RF3RIS BIT(26) /* RX FIFO 3/4 raw interrupt status */ +#define INTR1_RF4RIS BIT(27) /* RX FIFO full raw interrupt status */ +#define INTR1_TF1RIS BIT(28) /* TX FIFO 1/4 raw interrupt status */ +#define INTR1_TF2RIS BIT(29) /* TX FIFO 1/2 raw interrupt status */ +#define INTR1_TF3RIS BIT(30) /* TX FIFO 3/4 raw interrupt status */ +#define INTR1_TF4RIS BIT(31) /* TX FIFO empty raw interrupt status */ + +/* Frame valid number register (FRM_INFO) */ +#define FRM_INFO_RXFC GENMASK(5, 0) /* Valid frame number in RX FIFO */ +#define FRM_INFO_SSPD GENMASK(31, 16) /* Secondary sample point delay */ + +/* Interrupt register 2 (INTR2) */ +#define INTR2_TOIS BIT(0) /* RX FIFO time out interrupt status */ +#define INTR2_TOIM BIT(8) /* RX FIFO time out interrupt mask */ +#define INTR2_TOIC BIT(16) /* RX FIFO time out interrupt clear */ +#define INTR2_TORIS BIT(24) /* RX FIFO time out raw interrupt status */ + +/* RX information FIFO shadow register (RX_INFO_FIFO) */ +#define RX_INFO_FIFO_WNORF GENMASK(4, 0) /* Word (4-byte) number of current receive frame */ +#define RX_INFO_FIFO_RORF BIT(5) /* RTR value of current receive frame */ +#define RX_INFO_FIFO_FORF BIT(6) /* FDF value of current receive frame */ +#define RX_INFO_FIFO_IORF BIT(7) /* IDE value of current receive frame */ + +/* Arbitration Bits */ +#define CAN_ID1_MASK GENMASK(31, 21) /* Base identifer */ +/* Standard Remote Transmission Request */ +#define CAN_ID1_RTR_MASK BIT(20) +/* Extended Substitute remote TXreq */ +#define CAN_ID2_SRR_MASK BIT(20) +#define CAN_IDE_MASK BIT(19) /* IDentifier extension flag */ +#define CAN_ID2_MASK GENMASK(18, 1) /* Identifier extension */ +/* Extended frames remote TX request */ +#define CAN_ID2_RTR_MASK BIT(0) +#define CAN_ID1_FDF_MASK BIT(18) +#define CAN_ID1_DLC_MASK GENMASK(17, 14) +#define CANFD_ID1_BRS_MASK BIT(16) +#define CANFD_ID1_ESI_MASK BIT(15) +#define CANFD_ID1_DLC_MASK GENMASK(14, 11) + +#define CAN_ID2_FDF_MASK BIT(31) +#define CAN_ID2_DLC_MASK GENMASK(29, 26) +#define CANFD_ID2_BRS_MASK BIT(29) +#define CANFD_ID2_ESI_MASK BIT(28) +#define CANFD_ID2_DLC_MASK GENMASK(27, 24) + +#define CAN_ID1_DLC_OFF 14 +#define CANFD_ID1_DLC_OFF 11 +#define CAN_ID2_DLC_OFF 26 +#define CANFD_ID2_DLC_OFF 24 + +#define CAN_IDR_ID1_SHIFT 21 /* Standard Messg Identifier */ +#define CAN_IDR_ID2_SHIFT 1 /* Extended Message Identifier */ +#define CAN_IDR_SDLC_SHIFT 14 +#define CAN_IDR_EDLC_SHIFT 26 + +/* CANFD Standard msg padding 1 */ +#define CANFD_IDR_PAD_MASK 0x000007FF +#define CAN_IDR_PAD_MASK 0x00003FFF /* Standard msg padding 1 */ + +/** + * phytium_can_set_reg_bits - set a bit value to the device register + * @cdev: Driver private data structure + * @reg: Register offset + * @bs: The bit mask + * + * Read data from the particular CAN register + * Return: value read from the CAN register + */ +static void +phytium_can_set_reg_bits(const struct phytium_can_dev *cdev, + enum phytium_can_reg reg, u32 bs) +{ + u32 val = readl(cdev->base + reg); + + val |= bs; + writel(val, cdev->base + reg); +} + +/** + * phytium_can_clr_reg_bits - clear a bit value to the device register + * @cdev: Driver private data structure + * @reg: Register offset + * @bs: The bit mask + * + * Read data from the particular CAN register + * Return: value read from the CAN register + */ +static void +phytium_can_clr_reg_bits(const struct phytium_can_dev *cdev, + enum phytium_can_reg reg, u32 bs) +{ + u32 val = readl(cdev->base + reg); + + val &= ~bs; + writel(val, cdev->base + reg); +} + +static inline u32 phytium_can_read(const struct phytium_can_dev *cdev, enum phytium_can_reg reg) +{ + return readl(cdev->base + reg); +} + +static inline void phytium_can_write(const struct phytium_can_dev *cdev, enum phytium_can_reg reg, + u32 val) +{ + writel(val, cdev->base + reg); +} + +static inline void phytium_can_enable_all_interrupts(struct phytium_can_dev *cdev) +{ + phytium_can_write(cdev, CAN_INTR, INTR_EN_MASK); +} + +static inline void phytium_can_disable_all_interrupt(struct phytium_can_dev *cdev) +{ + phytium_can_write(cdev, CAN_INTR, 0x0); +} + +static int phytium_can_get_berr_counter(const struct net_device *dev, + struct can_berr_counter *bec) +{ + struct phytium_can_dev *cdev = netdev_priv(dev); + + bec->rxerr = phytium_can_read(cdev, CAN_ERR_CNT) & ERR_CNT_REC; + bec->txerr = (phytium_can_read(cdev, CAN_ERR_CNT) & ERR_CNT_TEC) >> 16; + + return 0; +} + +static int phytium_can_read_fifo(struct net_device *dev) +{ + struct net_device_stats *stats = &dev->stats; + struct phytium_can_dev *cdev = netdev_priv(dev); + struct canfd_frame *cf; + struct sk_buff *skb; + u32 id, dlc, i; + + /* Read the frame header from FIFO */ + id = phytium_can_read(cdev, CAN_RX_FIFO); + id = be32_to_cpup(&id); + if (id & CAN_IDE_MASK) { + /* Received an extended frame */ + dlc = phytium_can_read(cdev, CAN_RX_FIFO); + dlc = be32_to_cpup(&dlc); + if (dlc & CAN_ID2_FDF_MASK) + skb = alloc_canfd_skb(dev, &cf); + else + skb = alloc_can_skb(dev, (struct can_frame **)&cf); + + if (unlikely(!skb)) { + stats->rx_dropped++; + return 0; + } + + if (dlc & CAN_ID2_FDF_MASK) { + /* CAN FD extended frame */ + if (dlc & CANFD_ID2_BRS_MASK) + cf->flags |= CANFD_BRS; + if (dlc & CANFD_ID2_ESI_MASK) + cf->flags |= CANFD_ESI; + cf->len = can_dlc2len((dlc & CANFD_ID2_DLC_MASK) >> CANFD_ID2_DLC_OFF); + } else { + /* CAN extended frame */ + cf->len = get_can_dlc((dlc & CAN_ID2_DLC_MASK) >> CAN_ID2_DLC_OFF); + } + + cf->can_id = (id & CAN_ID1_MASK) >> 3; + cf->can_id |= (id & CAN_ID2_MASK) >> 1; + cf->can_id |= CAN_EFF_FLAG; + + if (id & CAN_ID2_RTR_MASK) + cf->can_id |= CAN_RTR_FLAG; + } else { + /* Received a standard frame */ + if (id & CAN_ID1_FDF_MASK) + skb = alloc_canfd_skb(dev, &cf); + else + skb = alloc_can_skb(dev, (struct can_frame **)&cf); + + if (unlikely(!skb)) { + stats->rx_dropped++; + return 0; + } + + if (id & CAN_ID1_FDF_MASK) { + /* CAN FD extended frame */ + if (id & CANFD_ID1_BRS_MASK) + cf->flags |= CANFD_BRS; + if (id & CANFD_ID1_ESI_MASK) + cf->flags |= CANFD_ESI; + cf->len = can_dlc2len((id & CANFD_ID1_DLC_MASK) >> CANFD_ID1_DLC_OFF); + } else { + /* CAN extended frame */ + cf->len = get_can_dlc((id & CAN_ID1_DLC_MASK) >> CAN_ID1_DLC_OFF); + } + + cf->can_id = (id & CAN_ID1_MASK) >> 21; + + if (id & CAN_ID1_RTR_MASK) + cf->can_id |= CAN_RTR_FLAG; + } + + if (!(cf->can_id & CAN_RTR_FLAG)) + /* Receive data frames */ + for (i = 0; i < cf->len; i += 4) + *(__be32 *)(cf->data + i) = phytium_can_read(cdev, CAN_RX_FIFO); + + stats->rx_packets++; + stats->rx_bytes += cf->len; + netif_receive_skb(skb); + + return 1; +} + +static int phytium_can_do_rx_poll(struct net_device *dev, int quota) +{ + struct phytium_can_dev *cdev = netdev_priv(dev); + u32 rxfs, pkts = 0; + + rxfs = phytium_can_read(cdev, CAN_FIFO_CNT) & FIFO_CNT_RFN; + if (!rxfs) { + netdev_dbg(dev, "no messages in RX FIFO\n"); + return 0; + } + + while ((rxfs != 0) && (quota > 0)) { + pkts += phytium_can_read_fifo(dev); + quota--; + rxfs = phytium_can_read(cdev, CAN_FIFO_CNT) & FIFO_CNT_RFN; + netdev_dbg(dev, "Next received %d frame again.\n", rxfs); + } + + if (pkts) + can_led_event(dev, CAN_LED_EVENT_RX); + + return pkts; +} + +static int phytium_can_rx_handler(struct net_device *dev, int quota) +{ + int work_done = 0; + int rx_work_or_err; + + /* Handle RX IRQ */ + rx_work_or_err = phytium_can_do_rx_poll(dev, (quota - work_done)); + if (rx_work_or_err < 0) + return rx_work_or_err; + + work_done += rx_work_or_err; + + return 0; +} + +static int phytium_can_poll(struct napi_struct *napi, int quota) +{ + struct net_device *dev = napi->dev; + struct phytium_can_dev *cdev = netdev_priv(dev); + int work_done; + unsigned long flags; + + netdev_dbg(dev, "The receive processing is going on !\n"); + + work_done = phytium_can_rx_handler(dev, quota); + + /* Don't re-enable interrupts if the driver had a fatal error + * (e.g., FIFO read failure) + */ + if (work_done >= 0 && work_done < quota) { + napi_complete_done(napi, work_done); + spin_lock_irqsave(&cdev->lock, flags); + phytium_can_set_reg_bits(cdev, CAN_INTR, INTR_REIE); + spin_unlock_irqrestore(&cdev->lock, flags); + } + + return work_done; +} + +static void phytium_can_write_frame(struct phytium_can_dev *cdev) +{ + struct canfd_frame *cf = (struct canfd_frame *)cdev->tx_skb->data; + struct net_device *dev = cdev->net; + struct net_device_stats *stats = &dev->stats; + struct sk_buff *skb = cdev->tx_skb; + u32 i, id, dlc = 0, frame_head[2] = {0, 0}; + u32 data_len; + + data_len = can_len2dlc(cf->len); + cdev->tx_skb = NULL; + + /* Watch carefully on the bit sequence */ + if (cf->can_id & CAN_EFF_FLAG) { + /* Extended CAN ID format */ + id = ((cf->can_id & CAN_EFF_MASK) << 1) & CAN_ID2_MASK; + id |= (((cf->can_id & CAN_EFF_MASK) >> + (CAN_EFF_ID_BITS - CAN_SFF_ID_BITS)) << + CAN_IDR_ID1_SHIFT) & CAN_ID1_MASK; + + /* The substibute remote TX request bit should be "1" + * for extended frames as in the Phytium CAN datasheet + */ + id |= CAN_IDE_MASK | CAN_ID2_SRR_MASK; + + if (cf->can_id & CAN_RTR_FLAG) + /* Extended frames remote TX request */ + id |= CAN_ID2_RTR_MASK; + if ((cdev->can.ctrlmode & CAN_CTRLMODE_FD) && + can_is_canfd_skb(skb)) + dlc = data_len << CANFD_ID2_DLC_OFF; + else + dlc = data_len << CAN_ID2_DLC_OFF; + + if (cdev->can.ctrlmode & CAN_CTRLMODE_FD) { + dlc |= CAN_ID2_FDF_MASK; + if (cf->flags & CANFD_BRS) + dlc |= CANFD_ID2_BRS_MASK; + if (cf->flags & CANFD_ESI) + dlc |= CANFD_ID2_ESI_MASK; + } + + frame_head[0] = cpu_to_be32p(&id); + frame_head[1] = cpu_to_be32p(&dlc); + + /* Write the Frame to Phytium CAN TX FIFO */ + phytium_can_write(cdev, CAN_TX_FIFO, frame_head[0]); + phytium_can_write(cdev, CAN_TX_FIFO, frame_head[1]); + netdev_dbg(dev, "Write atbitration field [0]:0x%x [1]:0x%x\n", + frame_head[0], frame_head[1]); + } else { + /* Standard CAN ID format */ + id = ((cf->can_id & CAN_SFF_MASK) << CAN_IDR_ID1_SHIFT) + & CAN_ID1_MASK; + + if (cf->can_id & CAN_RTR_FLAG) + /* Standard frames remote TX request */ + id |= CAN_ID1_RTR_MASK; + + if (cdev->can.ctrlmode & CAN_CTRLMODE_FD) + dlc = (data_len << CANFD_ID1_DLC_OFF) + | CANFD_IDR_PAD_MASK; + else + dlc = (data_len << CAN_ID1_DLC_OFF) | CAN_IDR_PAD_MASK; + + id |= dlc; + + if (cdev->can.ctrlmode & CAN_CTRLMODE_FD) { + id |= CAN_ID1_FDF_MASK; + if (cf->flags & CANFD_BRS) + id |= CANFD_ID1_BRS_MASK; + if (cf->flags & CANFD_ESI) + id |= CANFD_ID1_ESI_MASK; + } + + frame_head[0] = cpu_to_be32p(&id); + /* Write the Frame to Phytium CAN TX FIFO */ + phytium_can_write(cdev, CAN_TX_FIFO, frame_head[0]); + netdev_dbg(dev, "Write atbitration field [0] 0x%x\n", + frame_head[0]); + } + + if (!(cf->can_id & CAN_RTR_FLAG)) { + netdev_dbg(dev, "Write CAN data frame\n"); + for (i = 0; i < cf->len; i += 4) { + phytium_can_write(cdev, CAN_TX_FIFO, + *(__be32 *)(cf->data + i)); + netdev_dbg(dev, "[%d]:%x\n", i, + *(__be32 *)(cf->data + i)); + } + } + + stats->tx_bytes += cf->len; + stats->tx_packets++; + netdev_dbg(dev, "Trigger send message!\n"); + can_put_echo_skb(skb, dev, 0); + can_get_echo_skb(dev, 0); + return; +} + +static netdev_tx_t phytium_can_tx_handler(struct phytium_can_dev *cdev) +{ + struct net_device *dev = cdev->net; + u32 tx_fifo_used; + unsigned long flags; + + phytium_can_write_frame(cdev); + + /* Check if the TX buffer is full */ + tx_fifo_used = 4 * ((phytium_can_read(cdev, CAN_FIFO_CNT) & FIFO_CNT_TFN) >> 16); + if (cdev->can.ctrlmode & CAN_CTRLMODE_FD) { + if (CAN_FIFO_BYTE_LEN - tx_fifo_used <= KEEP_CANFD_FIFO_MIN_LEN) { + netif_stop_queue(dev); + spin_lock_irqsave(&cdev->lock, flags); + cdev->is_stop_queue_flag = STOP_QUEUE_TRUE; + spin_unlock_irqrestore(&cdev->lock, flags); + } + } else { + if (CAN_FIFO_BYTE_LEN - tx_fifo_used <= KEEP_CAN_FIFO_MIN_LEN) { + netif_stop_queue(dev); + spin_lock_irqsave(&cdev->lock, flags); + cdev->is_stop_queue_flag = STOP_QUEUE_TRUE; + spin_unlock_irqrestore(&cdev->lock, flags); + } + } + + return NETDEV_TX_OK; +} + +/** + * phytium_can_tx_interrupt - Tx Done Isr + * @ndev: net_device pointer + * @isr: Interrupt status register value + */ +static void phytium_can_tx_interrupt(struct net_device *ndev, u32 isr) +{ + struct phytium_can_dev *cdev = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + u32 tx_fifo_used = 0; + + if (isr & INTR_TEIS) { + phytium_can_set_reg_bits(cdev, CAN_INTR, INTR_TEIC); + } + + /* Check if the TX buffer is full */ + if (cdev->is_stop_queue_flag) { + tx_fifo_used = 4 * ((phytium_can_read(cdev, CAN_FIFO_CNT) & FIFO_CNT_TFN) >> 16); + if (cdev->can.ctrlmode & CAN_CTRLMODE_FD) { + if (CAN_FIFO_BYTE_LEN - tx_fifo_used > KEEP_CANFD_FIFO_MIN_LEN) { + netif_wake_queue(ndev); + cdev->is_stop_queue_flag = STOP_QUEUE_FALSE; + } + } else { + if (CAN_FIFO_BYTE_LEN - tx_fifo_used > KEEP_CAN_FIFO_MIN_LEN) { + netif_wake_queue(ndev); + cdev->is_stop_queue_flag = STOP_QUEUE_FALSE; + } + } + } + netdev_dbg(ndev, "Finish transform packets %lu\n", stats->tx_packets); + can_led_event(ndev, CAN_LED_EVENT_TX); +} + +static void phytium_can_err_interrupt(struct net_device *ndev, u32 isr) +{ + struct phytium_can_dev *cdev = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + struct can_frame *cf; + struct sk_buff *skb; + u32 txerr = 0, rxerr = 0; + + skb = alloc_can_err_skb(ndev, &cf); + + rxerr = phytium_can_read(cdev, CAN_ERR_CNT) & ERR_CNT_REC; + txerr = ((phytium_can_read(cdev, CAN_ERR_CNT) & ERR_CNT_TEC) >> 16); + + if (isr & INTR_BOIS) { + netdev_dbg(ndev, "bus_off %s: txerr :%u rxerr :%u\n", + __func__, txerr, rxerr); + cdev->can.state = CAN_STATE_BUS_OFF; + cdev->can.can_stats.bus_off++; + /* Leave device in Config Mode in bus-off state */ + phytium_can_write(cdev, CAN_CTRL, CTRL_RST); + can_bus_off(ndev); + if (skb) + cf->can_id |= CAN_ERR_BUSOFF; + } else if ((isr & INTR_PEIS) == INTR_PEIS) { + netdev_dbg(ndev, "error_passive %s: txerr :%u rxerr :%u\n", + __func__, txerr, rxerr); + cdev->can.state = CAN_STATE_ERROR_PASSIVE; + cdev->can.can_stats.error_passive++; + /* Clear interrupt condition */ + phytium_can_set_reg_bits(cdev, CAN_INTR, INTR_PEIC); + phytium_can_set_reg_bits(cdev, CAN_INTR, INTR_PWIC); + phytium_can_set_reg_bits(cdev, CAN_INTR, INTR_TEIC); + phytium_can_set_reg_bits(cdev, CAN_INTR, INTR_EIC); + if (skb) { + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = (rxerr > 127) ? + CAN_ERR_CRTL_RX_PASSIVE : + CAN_ERR_CRTL_TX_PASSIVE; + cf->data[6] = txerr; + cf->data[7] = rxerr; + } + } else if (isr & INTR_PWIS) { + netdev_dbg(ndev, "error_warning %s: txerr :%u rxerr :%u\n", + __func__, txerr, rxerr); + cdev->can.state = CAN_STATE_ERROR_WARNING; + cdev->can.can_stats.error_warning++; + phytium_can_set_reg_bits(cdev, CAN_INTR, INTR_PWIC); + phytium_can_set_reg_bits(cdev, CAN_INTR, INTR_TEIC); + phytium_can_set_reg_bits(cdev, CAN_INTR, INTR_EIC); + if (skb) { + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] |= (txerr > rxerr) ? + CAN_ERR_CRTL_TX_WARNING : + CAN_ERR_CRTL_RX_WARNING; + cf->data[6] = txerr; + cf->data[7] = rxerr; + } + } + + /* Check for RX FIFO Overflow interrupt */ + if (isr & INTR_RFIS) { + stats->rx_over_errors++; + stats->rx_errors++; + + if (skb) { + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW; + } + } + + if (skb) { + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); + } +} + +/** + * phytium_can_isr - CAN Isr + * @irq: irq number + * @dev_id: device id poniter + * + * This is the phytium CAN Isr. It checks for the type of interrupt + * and invokes the corresponding ISR. + * + * Return: + * * IRQ_NONE - If CAN device is in sleep mode, IRQ_HANDLED otherwise + */ +static irqreturn_t phytium_can_isr(int irq, void *dev_id) +{ + struct net_device *dev = (struct net_device *)dev_id; + struct phytium_can_dev *cdev = netdev_priv(dev); + u32 isr; + + /* Get the interrupt status */ + isr = phytium_can_read(cdev, CAN_INTR) & INTR_STATUS_MASK; + if (!isr) + return IRQ_NONE; + spin_lock(&cdev->lock); + /* Check for FIFO full interrupt and alarm */ + if ((isr & INTR_RFIS)) { + netdev_dbg(dev, "rx_fifo is full!.\n"); + phytium_can_clr_reg_bits(cdev, CAN_INTR, INTR_RFIE); + phytium_can_set_reg_bits(cdev, CAN_INTR, INTR_RFIC); + napi_schedule(&cdev->napi); + } + + /* Check for the type of error interrupt and Processing it */ + if (isr & (INTR_EIS | INTR_RFIS | INTR_BOIS)) { + phytium_can_clr_reg_bits(cdev, CAN_INTR, (INTR_EIE + | INTR_RFIE | INTR_BOIE)); + phytium_can_err_interrupt(dev, isr); + phytium_can_set_reg_bits(cdev, CAN_INTR, (INTR_EIC + | INTR_RFIC | INTR_BOIC)); + phytium_can_set_reg_bits(cdev, CAN_INTR, INTR_EIE | INTR_BOIE); + spin_unlock(&cdev->lock); + return IRQ_HANDLED; + } + + /* Check for Tx interrupt and Processing it */ + if (isr & INTR_TEIS) { + phytium_can_tx_interrupt(dev, isr); + } + + /* Check for the type of receive interrupt and Processing it */ + if (isr & INTR_REIS) { + phytium_can_clr_reg_bits(cdev, CAN_INTR, INTR_REIE); + phytium_can_set_reg_bits(cdev, CAN_INTR, INTR_REIC); + napi_schedule(&cdev->napi); + } + spin_unlock(&cdev->lock); + return IRQ_HANDLED; +} + +/** + * phytium_can_set_bittiming - CAN set bit timing routine + * @dev: Pointer to net_device structure + * + * This is the driver set bittiming routine. + * Return: 0 on success and failure value on error + */ +static int phytium_can_set_bittiming(struct net_device *dev) +{ + struct phytium_can_dev *cdev = netdev_priv(dev); + const struct can_bittiming *bt = &cdev->can.bittiming; + const struct can_bittiming *dbt = &cdev->can.data_bittiming; + u32 btr, dbtr; + u32 is_config_mode; + + /** + * Check whether Phytium CAN is in configuration mode. + * It cannot set bit timing if Phytium CAN is not in configuration mode. + */ + is_config_mode = phytium_can_read(cdev, CAN_CTRL) & CTRL_XFER; + if (is_config_mode) { + netdev_alert(dev, "BUG! Cannot set bittiming - CAN is not in config mode\n"); + return -EPERM; + } + + /* Setting Baud Rate prescalar value in BRPR Register */ + btr = (bt->brp - 1) << 16; + + /* Setting Time Segment 1 in BTR Register */ + btr |= (bt->prop_seg - 1) << 2; + + btr |= (bt->phase_seg1 - 1) << 5; + + /* Setting Time Segment 2 in BTR Register */ + btr |= (bt->phase_seg2 - 1) << 8; + + /* Setting Synchronous jump width in BTR Register */ + btr |= (bt->sjw - 1); + + dbtr = (dbt->brp - 1) << 16; + dbtr |= (dbt->prop_seg - 1) << 2; + dbtr |= (dbt->phase_seg1 - 1) << 5; + dbtr |= (dbt->phase_seg2 - 1) << 8; + dbtr |= (dbt->sjw - 1); + + if (cdev->can.ctrlmode & CAN_CTRLMODE_FD) { + phytium_can_write(cdev, CAN_ARB_RATE_CTRL, btr); + phytium_can_write(cdev, CAN_DAT_RATE_CTRL, dbtr); + } else { + phytium_can_write(cdev, CAN_ARB_RATE_CTRL, btr); + phytium_can_write(cdev, CAN_DAT_RATE_CTRL, btr); + } + + netdev_dbg(dev, "DAT=0x%08x, ARB=0x%08x\n", + phytium_can_read(cdev, CAN_DAT_RATE_CTRL), + phytium_can_read(cdev, CAN_ARB_RATE_CTRL)); + + return 0; +} + +/** + * phytium_can_start - This the drivers start routine + * @dev: Pointer to net_device structure + * + * This is the drivers start routine. + * Based on the State of the CAN device it puts + * the CAN device into a proper mode. + * + * Return: 0 on success and failure value on error + */ +static void phytium_can_start(struct net_device *dev) +{ + struct phytium_can_dev *cdev = netdev_priv(dev); + u32 ctrl; + + /* Disable transfer */ + ctrl = phytium_can_read(cdev, CAN_CTRL); + ctrl &= ~CTRL_XFER; + phytium_can_write(cdev, CAN_CTRL, ctrl); + + /* XXX: If CANFD, reset the controller */ + phytium_can_write(cdev, CAN_CTRL, (ctrl | CTRL_RST)); + + /* Bittiming setup */ + phytium_can_set_bittiming(dev); + + /* Acceptance identifier mask setup */ + phytium_can_write(cdev, CAN_ACC_ID0_MASK, ACC_IDX_MASK_AID_MASK); + phytium_can_write(cdev, CAN_ACC_ID1_MASK, ACC_IDX_MASK_AID_MASK); + phytium_can_write(cdev, CAN_ACC_ID2_MASK, ACC_IDX_MASK_AID_MASK); + phytium_can_write(cdev, CAN_ACC_ID3_MASK, ACC_IDX_MASK_AID_MASK); + ctrl |= CTRL_AIME; + + if (cdev->can.ctrlmode & CAN_CTRLMODE_FD) + ctrl |= CTRL_IOF | CTRL_FDCRC; + + phytium_can_write(cdev, CAN_CTRL, ctrl); + + cdev->can.state = CAN_STATE_ERROR_ACTIVE; + + phytium_can_enable_all_interrupts(cdev); + + if (cdev->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) + ctrl |= CTRL_XFER; + else + ctrl |= CTRL_XFER | CTRL_TXREQ; + + phytium_can_write(cdev, CAN_CTRL, ctrl); +} + +/** + * phytium_can_stop - Driver stop routine + * @dev: Pointer to net_device structure + * + * This is the drivers stop routine. It will disable the + * interrupts and put the device into configuration mode. + */ +static void phytium_can_stop(struct net_device *dev) +{ + struct phytium_can_dev *cdev = netdev_priv(dev); + u32 ctrl; + + /* Disable all interrupts */ + phytium_can_disable_all_interrupt(cdev); + + /* Disable transfer and switch to receive-only mode */ + ctrl = phytium_can_read(cdev, CAN_CTRL); + ctrl &= ~(CTRL_XFER | CTRL_TXREQ); + phytium_can_write(cdev, CAN_CTRL, ctrl); + + /* Set the state as STOPPED */ + cdev->can.state = CAN_STATE_STOPPED; +} + +static void phytium_can_clean(struct net_device *dev) +{ + struct phytium_can_dev *cdev = netdev_priv(dev); + + if (cdev->tx_skb) { + dev->stats.tx_errors++; + can_free_echo_skb(cdev->net, 0); + cdev->tx_skb = NULL; + } +} + +static int phytium_can_set_mode(struct net_device *dev, enum can_mode mode) +{ + switch (mode) { + case CAN_MODE_START: + phytium_can_clean(dev); + phytium_can_start(dev); + netif_wake_queue(dev); + break; + default: + return -EOPNOTSUPP; + } + return 0; +} + +/** + * phytium_can_open - Driver open routine + * @dev: Pointer to net_device structure + * + * This is the driver open routine. + * Return: 0 on success and failure value on error + */ +static int phytium_can_open(struct net_device *dev) +{ + struct phytium_can_dev *cdev = netdev_priv(dev); + int ret; + + ret = pm_runtime_get_sync(cdev->dev); + if (ret < 0) { + netdev_err(dev, "%s: pm_runtime_get failed(%d)\n", + __func__, ret); + return ret; + } + /* Open the CAN device */ + ret = open_candev(dev); + if (ret) { + netdev_err(dev, "failed to open can device\n"); + goto disable_clk; + } + + /* Register interrupt handler */ + ret = request_irq(dev->irq, phytium_can_isr, + IRQF_SHARED, dev->name, dev); + if (ret < 0) { + netdev_err(dev, "failed to request interrupt\n"); + goto fail; + } + + /* Start the controller */ + phytium_can_start(dev); + + can_led_event(dev, CAN_LED_EVENT_OPEN); + napi_enable(&cdev->napi); + cdev->is_stop_queue_flag = STOP_QUEUE_FALSE; + netif_start_queue(dev); + + return 0; + +fail: + pm_runtime_put(cdev->dev); + close_candev(dev); +disable_clk: + pm_runtime_put_sync(cdev->dev); + return ret; +} + +/** + * phytium_can_close - Driver close routine + * @dev: Pointer to net_device structure + * + * Return: 0 always + */ +static int phytium_can_close(struct net_device *dev) +{ + struct phytium_can_dev *cdev = netdev_priv(dev); + + netif_stop_queue(dev); + napi_disable(&cdev->napi); + + phytium_can_stop(dev); + free_irq(dev->irq, dev); + pm_runtime_put_sync(cdev->dev); + + close_candev(dev); + can_led_event(dev, CAN_LED_EVENT_STOP); + + return 0; +} + +/** + * phytium_can_start_xmit - Starts the transmission + * + * Return: 0 on success. + */ +static netdev_tx_t phytium_can_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct phytium_can_dev *cdev = netdev_priv(dev); + + if (can_dropped_invalid_skb(dev, skb)) + return NETDEV_TX_OK; + + cdev->tx_skb = skb; + + return phytium_can_tx_handler(cdev); +} + +static const struct net_device_ops phytium_can_netdev_ops = { + .ndo_open = phytium_can_open, + .ndo_stop = phytium_can_close, + .ndo_start_xmit = phytium_can_start_xmit, + .ndo_change_mtu = can_change_mtu, +}; + +static int register_phytium_can_dev(struct net_device *dev) +{ + dev->flags |= IFF_ECHO; + dev->netdev_ops = &phytium_can_netdev_ops; + + return register_candev(dev); +} + +static int phytium_can_dev_setup(struct phytium_can_dev *cdev) +{ + struct net_device *dev = cdev->net; + + netif_napi_add(dev, &cdev->napi, phytium_can_poll, 64); + + cdev->can.do_set_mode = phytium_can_set_mode; + cdev->can.do_get_berr_counter = phytium_can_get_berr_counter; + + cdev->can.ctrlmode_supported = CAN_CTRLMODE_LISTENONLY | + CAN_CTRLMODE_BERR_REPORTING; + cdev->can.bittiming_const = cdev->bit_timing; + + if (cdev->fdmode) { + cdev->can.ctrlmode_supported |= CAN_CTRLMODE_FD; + dev->mtu = CANFD_MTU; + cdev->can.ctrlmode = CAN_CTRLMODE_FD; + cdev->can.data_bittiming_const = cdev->bit_timing; + } + spin_lock_init(&cdev->lock); + return 0; +} + +struct phytium_can_dev *phytium_can_allocate_dev(struct device *dev, int sizeof_priv, + int tx_fifo_depth) +{ + struct phytium_can_dev *cdev = NULL; + struct net_device *net_dev; + + /* Allocate the can device struct */ + net_dev = alloc_candev(sizeof_priv, tx_fifo_depth); + if (!net_dev) { + dev_err(dev, "Failed to allocate CAN device.\n"); + goto out; + } + + cdev = netdev_priv(net_dev); + cdev->net = net_dev; + cdev->dev = dev; + SET_NETDEV_DEV(net_dev, dev); + +out: + return cdev; +} +EXPORT_SYMBOL(phytium_can_allocate_dev); + +void phytium_can_free_dev(struct net_device *net) +{ + free_candev(net); +} +EXPORT_SYMBOL(phytium_can_free_dev); + +int phytium_can_register(struct phytium_can_dev *cdev) +{ + int ret; + + ret = phytium_can_dev_setup(cdev); + if (ret) + goto fail; + + ret = register_phytium_can_dev(cdev->net); + if (ret) { + dev_err(cdev->dev, "registering %s failed (err=%d)\n", + cdev->net->name, ret); + goto fail; + } + + devm_can_led_init(cdev->net); + + dev_info(cdev->dev, "%s device registered (irq=%d)\n", + KBUILD_MODNAME, cdev->net->irq); + + /* Probe finished + * Stop clocks. They will be reactivated once the device is opened. + */ + pm_runtime_put_sync(cdev->dev); + + return 0; + +fail: + pm_runtime_put_sync(cdev->dev); + return ret; +} +EXPORT_SYMBOL(phytium_can_register); + +void phytium_can_unregister(struct phytium_can_dev *cdev) +{ + unregister_candev(cdev->net); +} +EXPORT_SYMBOL(phytium_can_unregister); + +int phytium_can_suspend(struct device *dev) +{ + struct net_device *ndev = dev_get_drvdata(dev); + struct phytium_can_dev *cdev = netdev_priv(ndev); + + if (netif_running(ndev)) { + netif_stop_queue(ndev); + netif_device_detach(ndev); + phytium_can_stop(ndev); + pm_runtime_put_sync(cdev->dev); + } + + cdev->can.state = CAN_STATE_SLEEPING; + + return 0; +} +EXPORT_SYMBOL(phytium_can_suspend); + +int phytium_can_resume(struct device *dev) +{ + struct net_device *ndev = dev_get_drvdata(dev); + struct phytium_can_dev *cdev = netdev_priv(ndev); + int ret; + + cdev->can.state = CAN_STATE_ERROR_ACTIVE; + + if (netif_running(ndev)) { + ret = pm_runtime_resume(cdev->dev); + if (ret) + return ret; + + phytium_can_start(ndev); + netif_device_attach(ndev); + netif_start_queue(ndev); + } + + return 0; +} +EXPORT_SYMBOL(phytium_can_resume); + +MODULE_AUTHOR("Cheng Quan "); +MODULE_AUTHOR("Chen Baozi "); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("CAN bus driver for Phytium CAN controller"); + diff --git a/target/linux/phytium/files-5.10/drivers/net/can/phytium/phytium_can.h b/target/linux/phytium/files-5.10/drivers/net/can/phytium/phytium_can.h new file mode 100644 index 000000000..400571b74 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/net/can/phytium/phytium_can.h @@ -0,0 +1,73 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Phytium CAN controller driver + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#ifndef _PHYTIUM_CAN_H_ +#define _PHYTIUM_CAN_H_ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define KEEP_CAN_FIFO_MIN_LEN 16 +#define KEEP_CANFD_FIFO_MIN_LEN 128 +#define CAN_FIFO_BYTE_LEN 256 +#define STOP_QUEUE_TRUE 1 +#define STOP_QUEUE_FALSE 0 + +enum phytium_can_ip_type { + PHYTIUM_CAN = 0, + PHYTIUM_CANFD, +}; + +struct phytium_can_devtype { + enum phytium_can_ip_type cantype; + const struct can_bittiming_const *bittiming_const; +}; + +struct phytium_can_dev { + struct can_priv can; + unsigned int tx_head; + unsigned int tx_tail; + unsigned int tx_max; + struct napi_struct napi; + struct net_device *net; + struct device *dev; + struct clk *clk; + + struct sk_buff *tx_skb; + + const struct can_bittiming_const *bit_timing; + spinlock_t lock; + int fdmode; + u32 isr; + u32 tx_fifo_depth; + unsigned int is_stop_queue_flag; + struct completion comp; + void __iomem *base; +}; + +struct phytium_can_dev *phytium_can_allocate_dev(struct device *dev, int sizeof_priv, + int tx_fifo_depth); +void phytium_can_free_dev(struct net_device *net); + +int phytium_can_register(struct phytium_can_dev *cdev); +void phytium_can_unregister(struct phytium_can_dev *cdev); + +int phytium_can_suspend(struct device *dev); +int phytium_can_resume(struct device *dev); +#endif /* _PHYTIUM_CAN_H_ */ diff --git a/target/linux/phytium/files-5.10/drivers/net/can/phytium/phytium_can_pci.c b/target/linux/phytium/files-5.10/drivers/net/can/phytium/phytium_can_pci.c new file mode 100644 index 000000000..ae4a6f877 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/net/can/phytium/phytium_can_pci.c @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Platform CAN bus driver for Phytium CAN controller + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#include + +#include "phytium_can.h" + +struct phytium_can_pci_config { + const struct phytium_can_devtype *devtype; + unsigned int clock_freq; + unsigned int tx_fifo_depth; +}; + +#define cdev2priv(dev) container_of(dev, struct phytium_can_pci, cdev) + +struct phytium_can_pci { + struct phytium_can_dev cdev; + + void __iomem *base; +}; + +static const struct can_bittiming_const phytium_bittiming_const_8192 = { + .name = "phytium_can", + .tseg1_min = 1, /* Time segment 1 = prop_seg + phase_seg1 */ + .tseg1_max = 16, + .tseg2_min = 1, /* Time segment 2 = phase_seg2 */ + .tseg2_max = 8, + .sjw_max = 4, /* Synchronisation jump width */ + .brp_min = 1, /* Bit-rate prescaler */ + .brp_max = 8192, + .brp_inc = 2, +}; + +static const struct phytium_can_devtype phytium_can_pci = { + .cantype = PHYTIUM_CAN, + .bittiming_const = &phytium_bittiming_const_8192, +}; + +static const struct phytium_can_pci_config phytium_can_pci_data = { + .devtype = &phytium_can_pci, + .clock_freq = 600000000, + .tx_fifo_depth = 64, +}; + +static int phytium_can_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + const struct phytium_can_pci_config *cfg; + struct phytium_can_dev *cdev; + struct phytium_can_pci *priv; + int ret; + + cfg = (const struct phytium_can_pci_config *)id->driver_data; + + ret = pcim_enable_device(pdev); + if (ret) + goto err; + + ret = pcim_iomap_regions(pdev, 0x1, pci_name(pdev)); + if (ret) + goto err; + + cdev = phytium_can_allocate_dev(&pdev->dev, sizeof(struct phytium_can_pci), + cfg->tx_fifo_depth); + if (!cdev) + return -ENOMEM; + + priv = cdev2priv(cdev); + priv->base = pcim_iomap_table(pdev)[0]; + + cdev->dev = &pdev->dev; + cdev->fdmode = cfg->devtype->cantype; + cdev->bit_timing = cfg->devtype->bittiming_const; + cdev->can.clock.freq = cfg->clock_freq; + cdev->tx_fifo_depth = cfg->tx_fifo_depth; + + cdev->tx_head = 0; + cdev->tx_tail = 0; + cdev->tx_max = cfg->tx_fifo_depth; + + cdev->base = priv->base; + cdev->net->irq = pdev->irq; + + pci_set_drvdata(pdev, cdev->net); + + if (!pm_runtime_enabled(cdev->dev)) + pm_runtime_enable(cdev->dev); + ret = pm_runtime_get_sync(cdev->dev); + if (ret < 0) { + netdev_err(cdev->net, "%s: pm_runtime_get failed(%d)\n", + __func__, ret); + goto err_pmdisable; + } + ret = phytium_can_register(cdev); + if (ret) + goto err; + + return 0; + +err_pmdisable: + pm_runtime_disable(&pdev->dev); +err: + return ret; +} + +static void phytium_can_pci_remove(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct phytium_can_dev *cdev = netdev_priv(dev); + + pm_runtime_disable(cdev->dev); + + phytium_can_unregister(cdev); + phytium_can_free_dev(cdev->net); +} + +static __maybe_unused int phytium_can_pci_suspend(struct device *dev) +{ + return phytium_can_suspend(dev); +} + +static __maybe_unused int phytium_can_pci_resume(struct device *dev) +{ + return phytium_can_resume(dev); +} + +static SIMPLE_DEV_PM_OPS(phytium_can_pci_pm_ops, + phytium_can_pci_suspend, phytium_can_pci_resume); + +static const struct pci_device_id phytium_can_pci_id_table[] = { + { PCI_VDEVICE(PHYTIUM, 0xdc2d), (kernel_ulong_t)&phytium_can_pci_data, }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(pci, phytium_can_pci_id_table); + +static struct pci_driver phytium_can_pci_driver = { + .name = KBUILD_MODNAME, + .probe = phytium_can_pci_probe, + .remove = phytium_can_pci_remove, + .id_table = phytium_can_pci_id_table, + .driver = { + .pm = &phytium_can_pci_pm_ops, + }, +}; + +module_pci_driver(phytium_can_pci_driver); + +MODULE_AUTHOR("Cheng Quan +#include +#include + +#include "phytium_can.h" + +#define cdev2priv(dev) container_of(dev, struct phytium_can_plat, cdev) + +struct phytium_can_plat { + struct phytium_can_dev cdev; + struct phytium_can_devtype *devtype; + + int irq; + void __iomem *reg_base; +}; + +static const struct can_bittiming_const phytium_bittiming_const_512 = { + .name = "phytium_can", + .tseg1_min = 1, /* Time segment 1 = prop_seg + phase_seg1 */ + .tseg1_max = 16, + .tseg2_min = 1, /* Time segment 2 = phase_seg2 */ + .tseg2_max = 8, + .sjw_max = 4, /* Synchronisation jump width */ + .brp_min = 1, /* Bit-rate prescaler */ + .brp_max = 512, + .brp_inc = 2, +}; + +static const struct can_bittiming_const phytium_bittiming_const_8192 = { + .name = "phytium_can", + .tseg1_min = 1, /* Time segment 1 = prop_seg + phase_seg1 */ + .tseg1_max = 16, + .tseg2_min = 1, /* Time segment 2 = phase_seg2 */ + .tseg2_max = 8, + .sjw_max = 4, /* Synchronisation jump width */ + .brp_min = 1, /* Bit-rate prescaler */ + .brp_max = 8192, + .brp_inc = 2, +}; + +static const struct phytium_can_devtype phytium_can_data = { + .cantype = PHYTIUM_CAN, + .bittiming_const = &phytium_bittiming_const_512, +}; + +static const struct phytium_can_devtype phytium_canfd_data = { + .cantype = PHYTIUM_CANFD, + .bittiming_const = &phytium_bittiming_const_8192, +}; + +#ifdef CONFIG_ACPI +static const struct acpi_device_id phytium_can_acpi_ids[] = { + { "PHYT000A", 0 }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(acpi, phytium_can_acpi_ids); +#endif + +#ifdef CONFIG_OF +static const struct of_device_id phytium_can_of_ids[] = { + { .compatible = "phytium,can", .data = &phytium_can_data }, + { .compatible = "phytium,canfd", .data = &phytium_canfd_data }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, phytium_can_of_ids); +#endif + + +static int phytium_can_plat_probe(struct platform_device *pdev) +{ + struct phytium_can_dev *cdev; + struct phytium_can_plat *priv; + struct resource *res; + const struct of_device_id *of_id; + const struct phytium_can_devtype *devtype = &phytium_can_data; + u32 tx_fifo_depth; + int ret; + const char *mode; + + ret = fwnode_property_read_u32(dev_fwnode(&pdev->dev), "tx-fifo-depth", &tx_fifo_depth); + if (ret) + tx_fifo_depth = 64; + + cdev = phytium_can_allocate_dev(&pdev->dev, sizeof(struct phytium_can_plat), + tx_fifo_depth); + if (!cdev) + return -ENOMEM; + + priv = cdev2priv(cdev); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->reg_base = devm_ioremap_resource(&pdev->dev, res); + priv->irq = platform_get_irq(pdev, 0); + if (IS_ERR(priv->reg_base) || cdev->net->irq < 0) { + ret = -EINVAL; + goto fail; + } + + if (pdev->dev.of_node) { + cdev->clk = devm_clk_get(&pdev->dev, "can_clk"); + if (IS_ERR(cdev->clk)) { + dev_err(&pdev->dev, "no clock found\n"); + ret = -ENODEV; + goto fail; + } + cdev->can.clock.freq = clk_get_rate(cdev->clk); + + of_id = of_match_device(phytium_can_of_ids, &pdev->dev); + if (of_id && of_id->data) + devtype = of_id->data; + } else if (has_acpi_companion(&pdev->dev)) { + ret = fwnode_property_read_u32(dev_fwnode(&pdev->dev), + "clock-frequency", + &cdev->can.clock.freq); + if (ret < 0) { + dev_err(&pdev->dev, "failed to get clock frequency.\n"); + goto fail; + } + ret = fwnode_property_read_string(dev_fwnode(&pdev->dev), "mode-select", &mode); + if (ret < 0) { + dev_info(&pdev->dev, "get mode-select ret: %d\n", ret); + } + else { + if (strncmp("canfd", mode, strlen("canfd")) == 0) { + dev_info(&pdev->dev, "use mode-select: canfd\n"); + devtype = &phytium_canfd_data; + } + } + } + + cdev->tx_fifo_depth = tx_fifo_depth; + + if (devtype->cantype == PHYTIUM_CANFD) + cdev->fdmode = 1; + else + cdev->fdmode = 0; + + if (fwnode_property_present(dev_fwnode(&pdev->dev), "extend_brp")) + cdev->bit_timing = &phytium_bittiming_const_8192; + else + cdev->bit_timing = devtype->bittiming_const; + cdev->can.bittiming_const = devtype->bittiming_const; + cdev->base = priv->reg_base; + cdev->net->irq = priv->irq; + + platform_set_drvdata(pdev, cdev->net); + + pm_runtime_enable(cdev->dev); + ret = phytium_can_register(cdev); + if (ret) + goto out_runtime_disable; + + return ret; + +out_runtime_disable: + pm_runtime_disable(cdev->dev); +fail: + phytium_can_free_dev(cdev->net); + return ret; +} + +static __maybe_unused int phytium_can_plat_suspend(struct device *dev) +{ + return phytium_can_suspend(dev); +} + +static __maybe_unused int phytium_can_plat_resume(struct device *dev) +{ + return phytium_can_resume(dev); +} + +static int phytium_can_plat_remove(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct phytium_can_dev *cdev = netdev_priv(dev); + + phytium_can_unregister(cdev); + + phytium_can_free_dev(cdev->net); + + return 0; +} + +static int __maybe_unused phytium_can_runtime_suspend(struct device *dev) +{ + struct net_device *ndev = dev_get_drvdata(dev); + struct phytium_can_dev *cdev = netdev_priv(ndev); + + clk_disable_unprepare(cdev->clk); + + return 0; +} + +static int __maybe_unused phytium_can_runtime_resume(struct device *dev) +{ + struct net_device *ndev = dev_get_drvdata(dev); + struct phytium_can_dev *cdev = netdev_priv(ndev); + + return clk_prepare_enable(cdev->clk); +} + +static const struct dev_pm_ops phytium_can_plat_pm_ops = { + SET_RUNTIME_PM_OPS(phytium_can_runtime_suspend, + phytium_can_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(phytium_can_suspend, phytium_can_resume) +}; + +static struct platform_driver phytium_can_plat_driver = { + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = of_match_ptr(phytium_can_of_ids), + .acpi_match_table = ACPI_PTR(phytium_can_acpi_ids), + .pm = &phytium_can_plat_pm_ops, + }, + .probe = phytium_can_plat_probe, + .remove = phytium_can_plat_remove, +}; + +module_platform_driver(phytium_can_plat_driver); + +MODULE_AUTHOR("Cheng Quan "); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Phytium CAN driver for IO Mapped controllers"); diff --git a/target/linux/phytium/files-5.10/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c b/target/linux/phytium/files-5.10/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c new file mode 100755 index 000000000..0dbf6eef7 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c @@ -0,0 +1,221 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Phytium SWMAC specific glue layer + * + * Copyright (c) 2022-2023, Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include + +#include "stmmac.h" +#include "stmmac_platform.h" + +static int phytium_get_mac_mode(struct fwnode_handle *fwnode) +{ + const char *pm; + int err, i; + + err = fwnode_property_read_string(fwnode, "mac-mode", &pm); + if (err < 0) + return err; + + for (i = 0; i < PHY_INTERFACE_MODE_MAX; i++) { + if (!strcasecmp(pm, phy_modes(i))) + return i; + } + + return -ENODEV; +} + +static int phytium_dwmac_acpi_phy(struct plat_stmmacenet_data *plat, + struct fwnode_handle *np, struct device *dev) +{ + plat->mdio_bus_data = devm_kzalloc(dev, + sizeof(struct stmmac_mdio_bus_data), + GFP_KERNEL); + + if (!plat->mdio_bus_data) + return -ENOMEM; + + return 0; +} + +static int phytium_dwmac_probe(struct platform_device *pdev) +{ + struct fwnode_handle *fwnode = dev_fwnode(&pdev->dev); + struct plat_stmmacenet_data *plat; + struct stmmac_resources stmmac_res; + struct device_node *np = pdev->dev.of_node; + u64 clk_freq; + char clk_name[20]; + int ret; + + plat = devm_kzalloc(&pdev->dev, sizeof(*plat), GFP_KERNEL); + if (!plat) + return -ENOMEM; + + plat->dma_cfg = devm_kzalloc(&pdev->dev, sizeof(*plat->dma_cfg), GFP_KERNEL); + if (!plat->dma_cfg) + return -ENOMEM; + + plat->axi = devm_kzalloc(&pdev->dev, sizeof(*plat->axi), GFP_KERNEL); + if (!plat->axi) + return -ENOMEM; + + plat->phy_interface = device_get_phy_mode(&pdev->dev); + if (plat->phy_interface < 0) + return plat->phy_interface; + + plat->interface = phytium_get_mac_mode(fwnode); + if (plat->interface < 0) + plat->interface = plat->phy_interface; + + /* Configure PHY if using device-tree */ + if (pdev->dev.of_node) { + plat->phy_node = of_parse_phandle(np, "phy-handle", 0); + plat->phylink_node = np; + } + + if (pdev->dev.of_node) { + plat->bus_id = of_alias_get_id(np, "ethernet"); + if (plat->bus_id < 0) + plat->bus_id = 0; + } else if (fwnode_property_read_u32(fwnode, "bus_id", &plat->bus_id)) { + plat->bus_id = 2; + } + + plat->phy_addr = -1; + plat->clk_csr = -1; + plat->has_gmac = 1; + plat->enh_desc = 1; + plat->bugged_jumbo = 1; + plat->pmt = 1; + plat->force_sf_dma_mode = 1; + + if (fwnode_property_read_u32(fwnode, "max-speed", &plat->max_speed)) + plat->max_speed = -1; + + if (fwnode_property_read_u32(fwnode, "max-frame-size", &plat->maxmtu)) + plat->maxmtu = JUMBO_LEN; + + if (fwnode_property_read_u32(fwnode, "snps,multicast-filter-bins", + &plat->multicast_filter_bins)) + plat->multicast_filter_bins = HASH_TABLE_SIZE; + + if (fwnode_property_read_u32(fwnode, "snps,perfect-filter-entries", + &plat->unicast_filter_entries)) + plat->unicast_filter_entries = 1; + + if (fwnode_property_read_u32(fwnode, "tx-fifo-depth", &plat->tx_fifo_size)) + plat->tx_fifo_size = 0x1000; + + if (fwnode_property_read_u32(fwnode, "rx-fifo-depth", &plat->rx_fifo_size)) + plat->rx_fifo_size = 0x1000; + + if (phytium_dwmac_acpi_phy(plat, fwnode, &pdev->dev)) + return -ENODEV; + + plat->rx_queues_to_use = 1; + plat->tx_queues_to_use = 1; + plat->rx_queues_cfg[0].mode_to_use = MTL_QUEUE_DCB; + plat->tx_queues_cfg[0].mode_to_use = MTL_QUEUE_DCB; + + if (fwnode_property_read_u64(fwnode, "clock-frequency", &clk_freq)) + clk_freq = 125000000; + + /* Set system clock */ + snprintf(clk_name, sizeof(clk_name), "%s-%d", "stmmaceth", plat->bus_id); + + plat->stmmac_clk = clk_register_fixed_rate(&pdev->dev, clk_name, NULL, 0, clk_freq); + if (IS_ERR(plat->stmmac_clk)) { + dev_warn(&pdev->dev, "Fail to register stmmac-clk\n"); + plat->stmmac_clk = NULL; + } + + ret = clk_prepare_enable(plat->stmmac_clk); + if (ret) { + clk_unregister_fixed_rate(plat->stmmac_clk); + return ret; + } + + plat->clk_ptp_rate = clk_get_rate(plat->stmmac_clk); + plat->clk_ptp_ref = NULL; + + if (fwnode_property_read_u32(fwnode, "snps,pbl", &plat->dma_cfg->pbl)) + plat->dma_cfg->pbl = 16; + + fwnode_property_read_u32(fwnode, "snps,txpbl", &plat->dma_cfg->txpbl); + fwnode_property_read_u32(fwnode, "snps,rxpbl", &plat->dma_cfg->rxpbl); + + plat->dma_cfg->pblx8 = !fwnode_property_read_bool(fwnode, "snps,no-pbl-x8"); + plat->dma_cfg->aal = fwnode_property_read_bool(fwnode, "snps,aal"); + plat->dma_cfg->fixed_burst = fwnode_property_read_bool(fwnode, "snps,fixed-burst"); + plat->dma_cfg->mixed_burst = fwnode_property_read_bool(fwnode, "snps,mixed-burst"); + + plat->axi->axi_lpi_en = false; + plat->axi->axi_xit_frm = false; + plat->axi->axi_wr_osr_lmt = 7; + plat->axi->axi_rd_osr_lmt = 7; + plat->axi->axi_blen[0] = 16; + + memset(&stmmac_res, 0, sizeof(stmmac_res)); + stmmac_res.addr = devm_platform_ioremap_resource(pdev, 0); + stmmac_res.irq = platform_get_irq(pdev, 0); + if (stmmac_res.irq < 0) { + dev_err(&pdev->dev, "IRQ not found.\n"); + return -ENXIO; + } + stmmac_res.wol_irq = stmmac_res.irq; + stmmac_res.lpi_irq = -1; + + return stmmac_dvr_probe(&pdev->dev, plat, &stmmac_res); +} + +int phytium_dwmac_remove(struct platform_device *pdev) +{ + int ret; + struct net_device *ndev = platform_get_drvdata(pdev); + struct stmmac_priv *priv = netdev_priv(ndev); + struct plat_stmmacenet_data *plat = priv->plat; + + ret = stmmac_pltfr_remove(pdev); + clk_unregister_fixed_rate(plat->stmmac_clk); + return ret; +} + +#ifdef CONFIG_OF +static const struct of_device_id phytium_dwmac_of_match[] = { + { .compatible = "phytium,gmac" }, + { } +}; +MODULE_DEVICE_TABLE(of, phytium_dwmac_of_match); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id phytium_dwmac_acpi_ids[] = { + { .id = "PHYT0004" }, + {} +}; +MODULE_DEVICE_TABLE(acpi, phytium_dwmac_acpi_ids); +#endif + +static struct platform_driver phytium_dwmac_driver = { + .probe = phytium_dwmac_probe, + .remove = phytium_dwmac_remove, + .driver = { + .name = "phytium-dwmac", + .pm = &stmmac_pltfr_pm_ops, + .of_match_table = of_match_ptr(phytium_dwmac_of_match), + .acpi_match_table = ACPI_PTR(phytium_dwmac_acpi_ids), + }, +}; +module_platform_driver(phytium_dwmac_driver); + +MODULE_AUTHOR("Chen Baozi "); +MODULE_DESCRIPTION("Phytium DWMAC specific glue layer"); +MODULE_LICENSE("GPL v2"); diff --git a/target/linux/phytium/files-5.10/drivers/net/phy/motorcomm.c b/target/linux/phytium/files-5.10/drivers/net/phy/motorcomm.c new file mode 100755 index 000000000..31a64698b --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/net/phy/motorcomm.c @@ -0,0 +1,2387 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * drivers/net/phy/motorcomm.c + * + * Driver for Motorcomm PHYs + * + * Author: yinghong.zhang + * + * Copyright (c) 2019 Motorcomm, Inc. + * + * 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. + * + * Support : Motorcomm Phys: + * Giga phys: yt8511, yt8521, yt8531, yt8614, yt8618 + * 100/10 Phys : yt8512, yt8512b, yt8510 + * Automotive 100Mb Phys : yt8010 + * Automotive 100/10 hyper range Phys: yt8510 + */ + +#include +#include +#include +#include +#include +#ifndef LINUX_VERSION_CODE +#include +#else +#define KERNEL_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c)) +#endif + +/* for wol feature, 20210604 */ +#include + +#define YT_LINUX_MAJOR 2 +#define YT_LINUX_MINOR 2 +#define YT_LINUX_SUBVERSION 8661 +#define YT_LINUX_VERSIONID "2.2.8661" + +/******************************************** + **** configuration section begin ***********/ + +/* if system depends on ethernet packet to restore from sleep, + * please define this macro to 1 otherwise, define it to 0. + */ +#define SYS_WAKEUP_BASED_ON_ETH_PKT 0 + +/* to enable system WOL feature of phy, please define this macro to 1 + * otherwise, define it to 0. + */ +#define YTPHY_WOL_FEATURE_ENABLE 0 + +/* some GMAC need clock input from PHY, for eg., 125M, + * please enable this macro + * by degault, it is set to 0 + * NOTE: this macro will need macro SYS_WAKEUP_BASED_ON_ETH_PKT to set to 1 + */ +#define GMAC_CLOCK_INPUT_NEEDED 0 + +/* the max number of yt8521 chip on pcb board + * the most case is only 1 chip per board, but + * by default, we support up to 8. + */ +#define YTPHY_BOARD_MAX_NUM_OF_CHIP_8521 8 +#define YTPHY_BOARD_MAX_NUM_OF_CHIP_8614 4 + +/* for YT8531 package A xtal init config */ +#define YTPHY8531A_XTAL_INIT (0) + +/**** configuration section end *********** + ******************************************/ + +/* no need to change below */ +#define MOTORCOMM_PHY_ID_MASK 0x00000fff +#define MOTORCOMM_PHY_ID_8531_MASK 0xffffffff +#define MOTORCOMM_MPHY_ID_MASK 0x0000ffff +#define MOTORCOMM_MPHY_ID_MASK_8614 0xffffffff +#define MOTORCOMM_PHY_ID_MASK_8821 0xffffffff + +#define PHY_ID_YT8010 0x00000309 +#define PHY_ID_YT8010AS 0x4f51eb19 +#define PHY_ID_YT8510 0x00000109 +#define PHY_ID_YT8511 0x0000010a +#define PHY_ID_YT8512 0x00000118 +#define PHY_ID_YT8512B 0x00000128 +#define PHY_ID_YT8521 0x0000011a +#define PHY_ID_YT8531S 0x4f51e91a +#define PHY_ID_YT8531 0x4f51e91b +#define PHY_ID_YT8614 0x4F51E899 +#define PHY_ID_YT8618 0x0000e889 +#define PHY_ID_YT8821 0x4f51ea10 + +#define REG_PHY_SPEC_STATUS 0x11 +#define REG_DEBUG_ADDR_OFFSET 0x1e +#define REG_DEBUG_DATA 0x1f + +#define YT8512_EXTREG_LED0 0x40c0 +#define YT8512_EXTREG_LED1 0x40c3 + +#define YT8512_EXTREG_SLEEP_CONTROL1 0x2027 + +#define YT_SOFTWARE_RESET 0x8000 + +#define YT8512_LED0_ACT_BLK_IND 0x1000 +#define YT8512_LED0_DIS_LED_AN_TRY 0x0001 +#define YT8512_LED0_BT_BLK_EN 0x0002 +#define YT8512_LED0_HT_BLK_EN 0x0004 +#define YT8512_LED0_COL_BLK_EN 0x0008 +#define YT8512_LED0_BT_ON_EN 0x0010 +#define YT8512_LED1_BT_ON_EN 0x0010 +#define YT8512_LED1_TXACT_BLK_EN 0x0100 +#define YT8512_LED1_RXACT_BLK_EN 0x0200 +#define YT8512_SPEED_MODE 0xc000 +#define YT8512_DUPLEX 0x2000 + +#define YT8512_SPEED_MODE_BIT 14 +#define YT8512_DUPLEX_BIT 13 +#define YT8512_EN_SLEEP_SW_BIT 15 + +#define YT8521_EXTREG_SLEEP_CONTROL1 0x27 +#define YT8521_EN_SLEEP_SW_BIT 15 + +#define YT8521_SPEED_MODE 0xc000 +#define YT8521_DUPLEX 0x2000 +#define YT8521_SPEED_MODE_BIT 14 +#define YT8521_DUPLEX_BIT 13 +#define YT8521_LINK_STATUS_BIT 10 + +/* based on yt8521 wol feature config register */ +#define YTPHY_UTP_INTR_REG 0x12 +/* WOL Feature Event Interrupt Enable */ +#define YTPHY_WOL_FEATURE_INTR BIT(6) + +/* Magic Packet MAC address registers */ +#define YTPHY_WOL_FEATURE_MACADDR2_4_MAGIC_PACKET 0xa007 +#define YTPHY_WOL_FEATURE_MACADDR1_4_MAGIC_PACKET 0xa008 +#define YTPHY_WOL_FEATURE_MACADDR0_4_MAGIC_PACKET 0xa009 + +#define YTPHY_WOL_FEATURE_REG_CFG 0xa00a +#define YTPHY_WOL_FEATURE_TYPE_CFG BIT(0) /* WOL TYPE Config */ +#define YTPHY_WOL_FEATURE_ENABLE_CFG BIT(3) /* WOL Enable Config */ +#define YTPHY_WOL_FEATURE_INTR_SEL_CFG BIT(6) /* WOL Event Interrupt Enable Config */ +#define YTPHY_WOL_FEATURE_WIDTH1_CFG BIT(1) /* WOL Pulse Width Config */ +#define YTPHY_WOL_FEATURE_WIDTH2_CFG BIT(2) /* WOL Pulse Width Config */ + +#define YTPHY_REG_SPACE_UTP 0 +#define YTPHY_REG_SPACE_FIBER 2 + +enum ytphy_wol_feature_trigger_type_e { + YTPHY_WOL_FEATURE_PULSE_TRIGGER, + YTPHY_WOL_FEATURE_LEVEL_TRIGGER, + YTPHY_WOL_FEATURE_TRIGGER_TYPE_MAX +}; + +enum ytphy_wol_feature_pulse_width_e { + YTPHY_WOL_FEATURE_672MS_PULSE_WIDTH, + YTPHY_WOL_FEATURE_336MS_PULSE_WIDTH, + YTPHY_WOL_FEATURE_168MS_PULSE_WIDTH, + YTPHY_WOL_FEATURE_84MS_PULSE_WIDTH, + YTPHY_WOL_FEATURE_PULSE_WIDTH_MAX +}; + +struct ytphy_wol_feature_cfg { + bool enable; + int type; + int width; +}; + +#if (YTPHY_WOL_FEATURE_ENABLE) +#undef SYS_WAKEUP_BASED_ON_ETH_PKT +#define SYS_WAKEUP_BASED_ON_ETH_PKT 1 +#endif + +/* YT8521 polling mode */ +#define YT8521_PHY_MODE_FIBER 1 //fiber mode only +#define YT8521_PHY_MODE_UTP 2 //utp mode only +#define YT8521_PHY_MODE_POLL 3 //fiber and utp, poll mode + +/* below are for bitmap */ +#define YT_PHY_MODE_FIBER 1 //fiber/sgmii mode only +#define YT_PHY_MODE_UTP 2 //utp mode only +#define YT_PHY_MODE_QSGMII 4 //qsgmii mode only +#define YT_PHY_MODE_POLL (YT_PHY_MODE_FIBER | YT_PHY_MODE_UTP | YT_PHY_MODE_QSGMII) //qsgmii, fiber/sgmii and utp, poll mode + +/* support automatically check polling mode for yt8521 + * for Fiber only system, please define YT8521_PHY_MODE_CURR 1 + * for UTP only system, please define YT8521_PHY_MODE_CURR 2 + * for combo system, please define YT8521_PHY_MODE_CURR 3 + */ +#define YTPHY_861X_ABC_VER 0 +#if (YTPHY_861X_ABC_VER) +static int yt8614_get_port_from_phydev(struct phy_device *phydev); +#endif +static int yt8521_hw_strap_polling(struct phy_device *phydev); +static int yt8614_hw_strap_polling(struct phy_device *phydev); +#define YT8521_PHY_MODE_CURR yt8521_hw_strap_polling(phydev) +#define YT8614_PHY_MODE_CURR yt8614_hw_strap_polling(phydev) + +#if (KERNEL_VERSION(4, 0, 0) > LINUX_VERSION_CODE) || (KERNEL_VERSION(5, 3, 0) < LINUX_VERSION_CODE) +static int ytphy_config_init(struct phy_device *phydev) +{ + int val; + + val = phy_read(phydev, 3); + + return 0; +} +#endif + + +#if (KERNEL_VERSION(5, 5, 0) > LINUX_VERSION_CODE) +static inline void phy_lock_mdio_bus(struct phy_device *phydev) +{ +#if (KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE) + mutex_lock(&phydev->bus->mdio_lock); +#else + mutex_lock(&phydev->mdio.bus->mdio_lock); +#endif +} + +static inline void phy_unlock_mdio_bus(struct phy_device *phydev) +{ +#if (KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE) + mutex_unlock(&phydev->bus->mdio_lock); +#else + mutex_unlock(&phydev->mdio.bus->mdio_lock); +#endif +} +#endif + +#if (KERNEL_VERSION(4, 16, 0) > LINUX_VERSION_CODE) +static inline int __phy_read(struct phy_device *phydev, u32 regnum) +{ +#if (KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE) + struct mii_bus *bus = phydev->bus; + int addr = phydev->addr; + return bus->read(bus, phydev->addr, regnum); +#else + struct mii_bus *bus = phydev->mdio.bus; + int addr = phydev->mdio.addr; +#endif + return bus->read(bus, addr, regnum); +} + +static inline int __phy_write(struct phy_device *phydev, u32 regnum, u16 val) +{ +#if (KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE) + struct mii_bus *bus = phydev->bus; + int addr = phydev->addr; +#else + struct mii_bus *bus = phydev->mdio.bus; + int addr = phydev->mdio.addr; +#endif + return bus->write(bus, addr, regnum, val); +} +#endif + +static int ytphy_read_ext(struct phy_device *phydev, u32 regnum) +{ + int ret; + + phy_lock_mdio_bus(phydev); + ret = __phy_write(phydev, REG_DEBUG_ADDR_OFFSET, regnum); + if (ret < 0) + goto err_handle; + + ret = __phy_read(phydev, REG_DEBUG_DATA); + if (ret < 0) + goto err_handle; + +err_handle: + phy_unlock_mdio_bus(phydev); + return ret; +} + +static int ytphy_write_ext(struct phy_device *phydev, u32 regnum, u16 val) +{ + int ret; + + phy_lock_mdio_bus(phydev); + ret = __phy_write(phydev, REG_DEBUG_ADDR_OFFSET, regnum); + if (ret < 0) + goto err_handle; + + ret = __phy_write(phydev, REG_DEBUG_DATA, val); + if (ret < 0) + goto err_handle; + +err_handle: + phy_unlock_mdio_bus(phydev); + return ret; +} + +static int ytphy_soft_reset(struct phy_device *phydev) +{ + int ret = 0, val = 0; + + val = phy_read(phydev, MII_BMCR); + if (val < 0) + return val; + + ret = phy_write(phydev, MII_BMCR, val | BMCR_RESET); + if (ret < 0) + return ret; + + return ret; +} + + +#if (YTPHY8531A_XTAL_INIT) +static int yt8531a_xtal_init(struct phy_device *phydev) +{ + int ret = 0; + int val = 0; + bool state = false; + + msleep(50); + + do { + ret = ytphy_write_ext(phydev, 0xa012, 0x88); + if (ret < 0) + return ret; + + msleep(100); + + val = ytphy_read_ext(phydev, 0xa012); + if (val < 0) + return val; + + usleep_range(10000, 20000); + } while (val != 0x88); + + ret = ytphy_write_ext(phydev, 0xa012, 0xc8); + if (ret < 0) + return ret; + + return ret; +} +#endif + +#if (KERNEL_VERSION(4, 0, 0) > LINUX_VERSION_CODE) +#else +int yt8010_soft_reset(struct phy_device *phydev) +{ + ytphy_soft_reset(phydev); + + return 0; +} + +int yt8010AS_soft_reset(struct phy_device *phydev) +{ + int ret = 0; + + /* sgmii */ + ytphy_write_ext(phydev, 0xe, 1); + ret = ytphy_soft_reset(phydev); + if (ret < 0) { + ytphy_write_ext(phydev, 0xe, 0); + return ret; + } + + /* utp */ + ytphy_write_ext(phydev, 0xe, 0); + ret = ytphy_soft_reset(phydev); + if (ret < 0) + return ret; + + return 0; +} +#endif + +#if (KERNEL_VERSION(3, 14, 79) < LINUX_VERSION_CODE) +int yt8010_aneg_done(struct phy_device *phydev) +{ + int val = 0; + + val = phy_read(phydev, 0x1); + val = phy_read(phydev, 0x1); + + return (val < 0) ? val : (val & BMSR_LSTATUS); +} +#endif + +static int yt8010_config_aneg(struct phy_device *phydev) +{ + phydev->speed = SPEED_100; + return 0; +} + +static int yt8010_read_status(struct phy_device *phydev) +{ + int ret = 0; + + ret = genphy_update_link(phydev); + if (ret) + return ret; + + /* for 8010, no definition mii reg 0x04, 0x11, here force 100/full */ + phydev->speed = SPEED_100; + phydev->duplex = DUPLEX_FULL; + + return 0; +} + +static int yt8010AS_config_init(struct phy_device *phydev) +{ + phydev->autoneg = AUTONEG_DISABLE; + + return 0; +} + +static int yt8512_led_init(struct phy_device *phydev) +{ + int ret; + int val; + int mask; + + val = ytphy_read_ext(phydev, YT8512_EXTREG_LED0); + if (val < 0) + return val; + + val |= YT8512_LED0_ACT_BLK_IND; + + mask = YT8512_LED0_DIS_LED_AN_TRY | YT8512_LED0_BT_BLK_EN | + YT8512_LED0_HT_BLK_EN | YT8512_LED0_COL_BLK_EN | + YT8512_LED0_BT_ON_EN; + val &= ~mask; + + ret = ytphy_write_ext(phydev, YT8512_EXTREG_LED0, val); + if (ret < 0) + return ret; + + val = ytphy_read_ext(phydev, YT8512_EXTREG_LED1); + if (val < 0) + return val; + + val |= YT8512_LED1_BT_ON_EN; + + mask = YT8512_LED1_TXACT_BLK_EN | YT8512_LED1_RXACT_BLK_EN; + val &= ~mask; + + ret = ytphy_write_ext(phydev, YT8512_EXTREG_LED1, val); + + return ret; +} + +static int yt8512_config_init(struct phy_device *phydev) +{ + int ret; + int val; + +#if (KERNEL_VERSION(4, 0, 0) > LINUX_VERSION_CODE) || (KERNEL_VERSION(5, 3, 0) < LINUX_VERSION_CODE) + ret = ytphy_config_init(phydev); +#else + ret = genphy_config_init(phydev); +#endif + if (ret < 0) + return ret; + + ret = yt8512_led_init(phydev); + + /* disable auto sleep */ + val = ytphy_read_ext(phydev, YT8512_EXTREG_SLEEP_CONTROL1); + if (val < 0) + return val; + + val &= (~BIT(YT8512_EN_SLEEP_SW_BIT)); + + ret = ytphy_write_ext(phydev, YT8512_EXTREG_SLEEP_CONTROL1, val); + if (ret < 0) + return ret; + + return ret; +} + +static int yt8512_read_status(struct phy_device *phydev) +{ + int ret; + int val; + int speed, speed_mode, duplex; + + ret = genphy_update_link(phydev); + if (ret) + return ret; + + val = phy_read(phydev, REG_PHY_SPEC_STATUS); + if (val < 0) + return val; + + duplex = (val & YT8512_DUPLEX) >> YT8512_DUPLEX_BIT; + speed_mode = (val & YT8512_SPEED_MODE) >> YT8512_SPEED_MODE_BIT; + switch (speed_mode) { + case 0: + speed = SPEED_10; + break; + case 1: + speed = SPEED_100; + break; + case 2: + case 3: + default: +#if (KERNEL_VERSION(4, 0, 0) > LINUX_VERSION_CODE) + speed = -1; +#else + speed = SPEED_UNKNOWN; +#endif + break; + } + + phydev->speed = speed; + phydev->duplex = duplex; + + return 0; +} + +#if (KERNEL_VERSION(4, 0, 0) > LINUX_VERSION_CODE) +#else +int yt8521_soft_reset(struct phy_device *phydev) +{ + int ret, val; + + if (YT8521_PHY_MODE_CURR == YT8521_PHY_MODE_UTP) { + ytphy_write_ext(phydev, 0xa000, 0); + ytphy_soft_reset(phydev); + } + + if (YT8521_PHY_MODE_CURR == YT8521_PHY_MODE_FIBER) { + ytphy_write_ext(phydev, 0xa000, 2); + ytphy_soft_reset(phydev); + + ytphy_write_ext(phydev, 0xa000, 0); + } + + if (YT8521_PHY_MODE_CURR == YT8521_PHY_MODE_POLL) { + val = ytphy_read_ext(phydev, 0xa001); + ytphy_write_ext(phydev, 0xa001, (val & ~0x8000)); + + ytphy_write_ext(phydev, 0xa000, 0); + ret = ytphy_soft_reset(phydev); + } + + return 0; +} +#endif + +#if GMAC_CLOCK_INPUT_NEEDED +static int ytphy_mii_rd_ext(struct mii_bus *bus, int phy_id, u32 regnum) +{ + int ret; + int val; + + ret = bus->write(bus, phy_id, REG_DEBUG_ADDR_OFFSET, regnum); + if (ret < 0) + return ret; + + val = bus->read(bus, phy_id, REG_DEBUG_DATA); + + return val; +} + +static int ytphy_mii_wr_ext(struct mii_bus *bus + int phy_id, + u32 regnum, + u16 val) +{ + int ret; + + ret = bus->write(bus, phy_id, REG_DEBUG_ADDR_OFFSET, regnum); + if (ret < 0) + return ret; + + ret = bus->write(bus, phy_id, REG_DEBUG_DATA, val); + + return ret; +} + +int yt8511_config_dis_txdelay(struct mii_bus *bus, int phy_id) +{ + int ret; + int val; + + /* disable auto sleep */ + val = ytphy_mii_rd_ext(bus, phy_id, 0x27); + if (val < 0) + return val; + + val &= (~BIT(15)); + + ret = ytphy_mii_wr_ext(bus, phy_id, 0x27, val); + if (ret < 0) + return ret; + + /* enable RXC clock when no wire plug */ + val = ytphy_mii_rd_ext(bus, phy_id, 0xc); + if (val < 0) + return val; + + /* ext reg 0xc b[7:4] + * Tx Delay time = 150ps * N - 250ps + */ + val &= ~(0xf << 4); + ret = ytphy_mii_wr_ext(bus, phy_id, 0xc, val); + + return ret; +} + +int yt8511_config_out_125m(struct mii_bus *bus, int phy_id) +{ + int ret; + int val; + + /* disable auto sleep */ + val = ytphy_mii_rd_ext(bus, phy_id, 0x27); + if (val < 0) + return val; + + val &= (~BIT(15)); + + ret = ytphy_mii_wr_ext(bus, phy_id, 0x27, val); + if (ret < 0) + return ret; + + /* enable RXC clock when no wire plug */ + val = ytphy_mii_rd_ext(bus, phy_id, 0xc); + if (val < 0) + return val; + + /* ext reg 0xc.b[2:1] + * 00-----25M from pll; + * 01---- 25M from xtl;(default) + * 10-----62.5M from pll; + * 11----125M from pll(here set to this value) + */ + val |= (3 << 1); + ret = ytphy_mii_wr_ext(bus, phy_id, 0xc, val); + +#ifdef YT_8511_INIT_TO_MASTER + /* for customer, please enable it based on demand. + * configure to master + */ + + /* master/slave config reg*/ + val = bus->read(bus, phy_id, 0x9); + /* to be manual config and force to be master */ + val |= (0x3<<11); + /* take effect until phy soft reset */ + ret = bus->write(bus, phy_id, 0x9, val); + if (ret < 0) + return ret; +#endif + + return ret; +} + +static int yt8511_config_init(struct phy_device *phydev) +{ + int ret; + +#if (KERNEL_VERSION(4, 0, 0) > LINUX_VERSION_CODE) || (KERNEL_VERSION(5, 3, 0) < LINUX_VERSION_CODE) + ret = ytphy_config_init(phydev); +#else + ret = genphy_config_init(phydev); +#endif + +#if (KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE) + netdev_info(phydev->attached_dev, "%s done, phy addr: %d\n", __func__, phydev->addr); +#else + netdev_info(phydev->attached_dev, "%s done, phy addr: %d\n", __func__, phydev->mdio.addr); +#endif + + return ret; +} +#endif /* GMAC_CLOCK_INPUT_NEEDED */ + +#if (YTPHY_WOL_FEATURE_ENABLE) +static int ytphy_switch_reg_space(struct phy_device *phydev, int space) +{ + int ret; + + if (space == YTPHY_REG_SPACE_UTP) + ret = ytphy_write_ext(phydev, 0xa000, 0); + else + ret = ytphy_write_ext(phydev, 0xa000, 2); + + return ret; +} + +static int ytphy_wol_feature_enable_cfg(struct phy_device *phydev, + struct ytphy_wol_feature_cfg wol_cfg) +{ + int ret = 0; + int val = 0; + + val = ytphy_read_ext(phydev, YTPHY_WOL_FEATURE_REG_CFG); + if (val < 0) + return val; + + if (wol_cfg.enable) { + val |= YTPHY_WOL_FEATURE_ENABLE_CFG; + + if (wol_cfg.type == YTPHY_WOL_FEATURE_LEVEL_TRIGGER) { + val &= ~YTPHY_WOL_FEATURE_TYPE_CFG; + val &= ~YTPHY_WOL_FEATURE_INTR_SEL_CFG; + } else if (wol_cfg.type == YTPHY_WOL_FEATURE_PULSE_TRIGGER) { + val |= YTPHY_WOL_FEATURE_TYPE_CFG; + val |= YTPHY_WOL_FEATURE_INTR_SEL_CFG; + + if (wol_cfg.width == YTPHY_WOL_FEATURE_84MS_PULSE_WIDTH) { + val &= ~YTPHY_WOL_FEATURE_WIDTH1_CFG; + val &= ~YTPHY_WOL_FEATURE_WIDTH2_CFG; + } else if (wol_cfg.width == YTPHY_WOL_FEATURE_168MS_PULSE_WIDTH) { + val |= YTPHY_WOL_FEATURE_WIDTH1_CFG; + val &= ~YTPHY_WOL_FEATURE_WIDTH2_CFG; + } else if (wol_cfg.width == YTPHY_WOL_FEATURE_336MS_PULSE_WIDTH) { + val &= ~YTPHY_WOL_FEATURE_WIDTH1_CFG; + val |= YTPHY_WOL_FEATURE_WIDTH2_CFG; + } else if (wol_cfg.width == YTPHY_WOL_FEATURE_672MS_PULSE_WIDTH) { + val |= YTPHY_WOL_FEATURE_WIDTH1_CFG; + val |= YTPHY_WOL_FEATURE_WIDTH2_CFG; + } + } + } else { + val &= ~YTPHY_WOL_FEATURE_ENABLE_CFG; + val &= ~YTPHY_WOL_FEATURE_INTR_SEL_CFG; + } + + ret = ytphy_write_ext(phydev, YTPHY_WOL_FEATURE_REG_CFG, val); + if (ret < 0) + return ret; + + return 0; +} + +static void ytphy_wol_feature_get(struct phy_device *phydev, + struct ethtool_wolinfo *wol) +{ + int val = 0; + + wol->supported = WAKE_MAGIC; + wol->wolopts = 0; + + val = ytphy_read_ext(phydev, YTPHY_WOL_FEATURE_REG_CFG); + if (val < 0) + return; + + if (val & YTPHY_WOL_FEATURE_ENABLE_CFG) + wol->wolopts |= WAKE_MAGIC; + + //return; +} + +static int ytphy_wol_feature_set(struct phy_device *phydev, + struct ethtool_wolinfo *wol) +{ + int ret, curr_reg_space, val; + struct ytphy_wol_feature_cfg wol_cfg; + struct net_device *p_attached_dev = phydev->attached_dev; + + memset(&wol_cfg, 0, sizeof(struct ytphy_wol_feature_cfg)); + curr_reg_space = ytphy_read_ext(phydev, 0xa000); + if (curr_reg_space < 0) + return curr_reg_space; + + /* Switch to phy UTP page */ + ret = ytphy_switch_reg_space(phydev, YTPHY_REG_SPACE_UTP); + if (ret < 0) + return ret; + + if (wol->wolopts & WAKE_MAGIC) { + /* Enable the WOL feature interrupt */ + val = phy_read(phydev, YTPHY_UTP_INTR_REG); + val |= YTPHY_WOL_FEATURE_INTR; + ret = phy_write(phydev, YTPHY_UTP_INTR_REG, val); + if (ret < 0) + return ret; + + /* Set the WOL feature config */ + wol_cfg.enable = true; + wol_cfg.type = YTPHY_WOL_FEATURE_PULSE_TRIGGER; + wol_cfg.width = YTPHY_WOL_FEATURE_672MS_PULSE_WIDTH; + ret = ytphy_wol_feature_enable_cfg(phydev, wol_cfg); + if (ret < 0) + return ret; + + /* Store the device address for the magic packet */ + ret = ytphy_write_ext(phydev, YTPHY_WOL_FEATURE_MACADDR2_4_MAGIC_PACKET, + ((p_attached_dev->dev_addr[0] << 8) | + p_attached_dev->dev_addr[1])); + if (ret < 0) + return ret; + ret = ytphy_write_ext(phydev, YTPHY_WOL_FEATURE_MACADDR1_4_MAGIC_PACKET, + ((p_attached_dev->dev_addr[2] << 8) | + p_attached_dev->dev_addr[3])); + if (ret < 0) + return ret; + ret = ytphy_write_ext(phydev, YTPHY_WOL_FEATURE_MACADDR0_4_MAGIC_PACKET, + ((p_attached_dev->dev_addr[4] << 8) | + p_attached_dev->dev_addr[5])); + if (ret < 0) + return ret; + } else { + wol_cfg.enable = false; + wol_cfg.type = YTPHY_WOL_FEATURE_TRIGGER_TYPE_MAX; + wol_cfg.width = YTPHY_WOL_FEATURE_PULSE_WIDTH_MAX; + ret = ytphy_wol_feature_enable_cfg(phydev, wol_cfg); + if (ret < 0) + return ret; + } + + /* Recover to previous register space page */ + ret = ytphy_switch_reg_space(phydev, curr_reg_space); + if (ret < 0) + return ret; + + return 0; +} +#endif /*(YTPHY_WOL_FEATURE_ENABLE)*/ + +static int yt8521_hw_strap_polling(struct phy_device *phydev) +{ + int val = 0; + + val = ytphy_read_ext(phydev, 0xa001) & 0x7; + switch (val) { + case 1: + case 4: + case 5: + return YT8521_PHY_MODE_FIBER; + case 2: + case 6: + case 7: + return YT8521_PHY_MODE_POLL; + case 3: + case 0: + default: + return YT8521_PHY_MODE_UTP; + } +} + + +static int yt8521_config_init(struct phy_device *phydev) +{ + int ret; + int val, hw_strap_mode; + +#if (YTPHY_WOL_FEATURE_ENABLE) + struct ethtool_wolinfo wol; + + /* set phy wol enable */ + memset(&wol, 0x0, sizeof(struct ethtool_wolinfo)); + wol.wolopts |= WAKE_MAGIC; + ytphy_wol_feature_set(phydev, &wol); +#endif + if (phydev->force_mode) { + hw_strap_mode = ytphy_read_ext(phydev, 0xa001) & 0x7; + hw_strap_mode = hw_strap_mode & 0x7ff8; + hw_strap_mode = hw_strap_mode |0x140; + ytphy_write_ext(phydev, 0xa001, hw_strap_mode); + } + + phydev->irq = PHY_POLL; + /* NOTE: this function should not be called more than one for each chip. */ + hw_strap_mode = ytphy_read_ext(phydev, 0xa001) & 0x7; + + ytphy_write_ext(phydev, 0xa000, 0); +#if (KERNEL_VERSION(4, 0, 0) > LINUX_VERSION_CODE) || (KERNEL_VERSION(5, 3, 0) < LINUX_VERSION_CODE) + ret = ytphy_config_init(phydev); +#else + ret = genphy_config_init(phydev); +#endif + if (ret < 0) + return ret; + + /* disable auto sleep */ + val = ytphy_read_ext(phydev, YT8521_EXTREG_SLEEP_CONTROL1); + if (val < 0) + return val; + + val &= (~BIT(YT8521_EN_SLEEP_SW_BIT)); + + ret = ytphy_write_ext(phydev, YT8521_EXTREG_SLEEP_CONTROL1, val); + if (ret < 0) + return ret; + + /* enable RXC clock when no wire plug */ + val = ytphy_read_ext(phydev, 0xc); + if (val < 0) + return val; + val &= ~(1 << 12); + ret = ytphy_write_ext(phydev, 0xc, val); + if (ret < 0) + return ret; + +#if (KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE) + netdev_info(phydev->attached_dev, "%s done, phy addr: %d, strap mode = %d, polling mode = %d\n", + __func__, phydev->addr, hw_strap_mode, yt8521_hw_strap_polling(phydev)); +#else + netdev_info(phydev->attached_dev, "%s done, phy addr: %d, strap mode = %d, polling mode = %d\n", + __func__, phydev->mdio.addr, hw_strap_mode, yt8521_hw_strap_polling(phydev)); +#endif + return ret; +} + +/* for fiber mode, there is no 10M speed mode and + * this function is for this purpose. + */ +static int yt8521_adjust_status(struct phy_device *phydev, int val, int is_utp) +{ + int speed_mode, duplex; +#if (KERNEL_VERSION(4, 0, 0) > LINUX_VERSION_CODE) + int speed = -1; +#else + int speed = SPEED_UNKNOWN; +#endif + + if (is_utp) + duplex = (val & YT8512_DUPLEX) >> YT8521_DUPLEX_BIT; + else + duplex = 1; + speed_mode = (val & YT8521_SPEED_MODE) >> YT8521_SPEED_MODE_BIT; + switch (speed_mode) { + case 0: + if (is_utp) + speed = SPEED_10; + break; + case 1: + speed = SPEED_100; + break; + case 2: + speed = SPEED_1000; + break; + case 3: + break; + default: +#if (KERNEL_VERSION(4, 0, 0) > LINUX_VERSION_CODE) + speed = -1; +#else + speed = SPEED_UNKNOWN; +#endif + break; + } + + phydev->speed = speed; + phydev->duplex = duplex; + + return 0; +} + +/* for fiber mode, when speed is 100M, there is no definition for + * autonegotiation, and this function handles this case and return + * 1 per linux kernel's polling. + */ +int yt8521_aneg_done(struct phy_device *phydev) +{ + int link_fiber = 0, link_utp = 0; + + /* reading Fiber */ + ytphy_write_ext(phydev, 0xa000, 2); + link_fiber = !!(phy_read(phydev, REG_PHY_SPEC_STATUS) & (BIT(YT8521_LINK_STATUS_BIT))); + + /* reading UTP */ + ytphy_write_ext(phydev, 0xa000, 0); + if (!link_fiber) + link_utp = !!(phy_read(phydev, REG_PHY_SPEC_STATUS) & (BIT(YT8521_LINK_STATUS_BIT))); + +#if (KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE) + netdev_info(phydev->attached_dev, "%s, phy addr: %d, link_fiber: %d, link_utp: %d\n", + __func__, phydev->addr, link_fiber, link_utp); +#else + netdev_info(phydev->attached_dev, "%s, phy addr: %d, link_fiber: %d, link_utp: %d\n", + __func__, phydev->mdio.addr, link_fiber, link_utp); +#endif + return !!(link_fiber | link_utp); +} + +static int yt8521_read_status(struct phy_device *phydev) +{ + int ret; + int val; + int yt8521_fiber_latch_val; + int yt8521_fiber_curr_val; + int link; + int link_utp = 0, link_fiber = 0; + + if (YT8521_PHY_MODE_CURR != YT8521_PHY_MODE_FIBER) { + /* reading UTP */ + ret = ytphy_write_ext(phydev, 0xa000, 0); + if (ret < 0) + return ret; + + val = phy_read(phydev, REG_PHY_SPEC_STATUS); + if (val < 0) + return val; + + link = val & (BIT(YT8521_LINK_STATUS_BIT)); + if (link) { + link_utp = 1; + yt8521_adjust_status(phydev, val, 1); + } else { + link_utp = 0; + } + } //(YT8521_PHY_MODE_CURR != YT8521_PHY_MODE_FIBER) + + if (YT8521_PHY_MODE_CURR != YT8521_PHY_MODE_UTP) { + /* reading Fiber */ + ret = ytphy_write_ext(phydev, 0xa000, 2); + if (ret < 0) + return ret; + + val = phy_read(phydev, REG_PHY_SPEC_STATUS); + if (val < 0) + return val; + + //note: below debug information is used to check multiple PHy ports. + + /* for fiber, from 1000m to 100m, there is not link down from 0x11, + * and check reg 1 to identify such case this is important for Linux + * kernel for that, missing linkdown event will cause problem. + */ + yt8521_fiber_latch_val = phy_read(phydev, MII_BMSR); + yt8521_fiber_curr_val = phy_read(phydev, MII_BMSR); + link = val & (BIT(YT8521_LINK_STATUS_BIT)); + if (link && yt8521_fiber_latch_val != yt8521_fiber_curr_val) { + link = 0; +#if (KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE) + netdev_info(phydev->attached_dev, "%s, phy addr: %d, fiber link down detect, latch = %04x, curr = %04x\n", + __func__, phydev->addr, yt8521_fiber_latch_val, yt8521_fiber_curr_val); +#else + netdev_info(phydev->attached_dev, "%s, phy addr: %d, fiber link down detect, latch = %04x, curr = %04x\n", + __func__, phydev->mdio.addr, yt8521_fiber_latch_val, yt8521_fiber_curr_val); +#endif + } + + if (link) { + link_fiber = 1; + yt8521_adjust_status(phydev, val, 0); + } else { + link_fiber = 0; + } + } //(YT8521_PHY_MODE_CURR != YT8521_PHY_MODE_UTP) + + if (link_utp || link_fiber) { + if (phydev->link == 0) +#if (KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE) + netdev_info(phydev->attached_dev, "%s, phy addr: %d, link up, media: %s, mii reg 0x11 = 0x%x\n", + __func__, phydev->addr, (link_utp && link_fiber) ? "UNKNOWN MEDIA" : (link_utp ? "UTP" : "Fiber"), (unsigned int)val); +#else + netdev_info(phydev->attached_dev, "%s, phy addr: %d, link up, media: %s, mii reg 0x11 = 0x%x\n", + __func__, phydev->mdio.addr, (link_utp && link_fiber) ? "UNKNOWN MEDIA" : (link_utp ? "UTP" : "Fiber"), (unsigned int)val); +#endif + phydev->link = 1; + } else { + if (phydev->link == 1) +#if (KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE) + netdev_info(phydev->attached_dev, "%s, phy addr: %d, link down\n", __func__, phydev->addr); +#else + netdev_info(phydev->attached_dev, "%s, phy addr: %d, link down\n", __func__, phydev->mdio.addr); +#endif + phydev->link = 0; + } + + if (YT8521_PHY_MODE_CURR != YT8521_PHY_MODE_FIBER) { //utp or combo + if (link_fiber) + ytphy_write_ext(phydev, 0xa000, 2); + if (link_utp) + ytphy_write_ext(phydev, 0xa000, 0); + } + return 0; +} + +int yt8521_suspend(struct phy_device *phydev) +{ +#if !(SYS_WAKEUP_BASED_ON_ETH_PKT) + int value; + +#if (KERNEL_VERSION(4, 0, 0) > LINUX_VERSION_CODE) + mutex_lock(&phydev->lock); +#else + /* no need lock in 4.19 */ +#endif + + ytphy_write_ext(phydev, 0xa000, 0); + value = phy_read(phydev, MII_BMCR); + phy_write(phydev, MII_BMCR, value | BMCR_PDOWN); + + ytphy_write_ext(phydev, 0xa000, 2); + value = phy_read(phydev, MII_BMCR); + phy_write(phydev, MII_BMCR, value | BMCR_PDOWN); + + ytphy_write_ext(phydev, 0xa000, 0); + +#if (KERNEL_VERSION(4, 0, 0) > LINUX_VERSION_CODE) + mutex_unlock(&phydev->lock); +#else + /* no need lock/unlock in 4.19 */ +#endif +#endif /*!(SYS_WAKEUP_BASED_ON_ETH_PKT)*/ + + return 0; +} + +int yt8521_resume(struct phy_device *phydev) +{ + int value, ret; + + /* disable auto sleep */ + value = ytphy_read_ext(phydev, YT8521_EXTREG_SLEEP_CONTROL1); + if (value < 0) + return value; + + value &= (~BIT(YT8521_EN_SLEEP_SW_BIT)); + + ret = ytphy_write_ext(phydev, YT8521_EXTREG_SLEEP_CONTROL1, value); + if (ret < 0) + return ret; + +#if !(SYS_WAKEUP_BASED_ON_ETH_PKT) +#if (KERNEL_VERSION(4, 0, 0) > LINUX_VERSION_CODE) + mutex_lock(&phydev->lock); +#else + /* no need lock/unlock in 4.19 */ +#endif + + /* power down both sds & phy in suspend, power up both too */ + ytphy_write_ext(phydev, 0xa000, 0); + value = phy_read(phydev, MII_BMCR); + phy_write(phydev, MII_BMCR, value & ~BMCR_PDOWN); + + ytphy_write_ext(phydev, 0xa000, 2); + value = phy_read(phydev, MII_BMCR); + phy_write(phydev, MII_BMCR, value & ~BMCR_PDOWN); + + ytphy_write_ext(phydev, 0xa000, 0); + +#if (KERNEL_VERSION(4, 0, 0) > LINUX_VERSION_CODE) + mutex_unlock(&phydev->lock); +#else + /* no need lock/unlock in 4.19 */ +#endif +#endif /*!(SYS_WAKEUP_BASED_ON_ETH_PKT)*/ + + return 0; +} + +static int yt8531S_config_init(struct phy_device *phydev) +{ + int ret = 0; + +#if (YTPHY8531A_XTAL_INIT) + ret = yt8531a_xtal_init(phydev); + if (ret < 0) + return ret; +#endif + + ret = yt8521_config_init(phydev); + + return ret; +} + +static int yt8531_config_init(struct phy_device *phydev) +{ + int ret = 0; + +#if (YTPHY8531A_XTAL_INIT) + ret = yt8531a_xtal_init(phydev); + if (ret < 0) + return ret; +#endif + +#if (KERNEL_VERSION(4, 0, 0) > LINUX_VERSION_CODE) || (KERNEL_VERSION(5, 3, 0) < LINUX_VERSION_CODE) + ret = ytphy_config_init(phydev); +#else + ret = genphy_config_init(phydev); +#endif + if (ret < 0) + return ret; + + return 0; +} + +#if (KERNEL_VERSION(4, 0, 0) > LINUX_VERSION_CODE) +#else +int yt8618_soft_reset(struct phy_device *phydev) +{ + int ret; + + ytphy_write_ext(phydev, 0xa000, 0); + ret = ytphy_soft_reset(phydev); + if (ret < 0) + return ret; + + return 0; +} + +int yt8614_soft_reset(struct phy_device *phydev) +{ + int ret; + + /* qsgmii */ + ytphy_write_ext(phydev, 0xa000, 2); + ret = ytphy_soft_reset(phydev); + if (ret < 0) { + ytphy_write_ext(phydev, 0xa000, 0); + return ret; + } + + /* sgmii */ + ytphy_write_ext(phydev, 0xa000, 3); + ret = ytphy_soft_reset(phydev); + if (ret < 0) { + ytphy_write_ext(phydev, 0xa000, 0); + return ret; + } + + /* utp */ + ytphy_write_ext(phydev, 0xa000, 0); + ret = ytphy_soft_reset(phydev); + if (ret < 0) + return ret; + + return 0; +} +#endif + +static int yt8618_config_init(struct phy_device *phydev) +{ + int ret; + int val; + unsigned int retries = 12; +#if (YTPHY_861X_ABC_VER) + int port = 0; +#endif + + phydev->irq = PHY_POLL; + +#if (YTPHY_861X_ABC_VER) + port = yt8614_get_port_from_phydev(phydev); +#endif + + ytphy_write_ext(phydev, 0xa000, 0); +#if (KERNEL_VERSION(4, 0, 0) > LINUX_VERSION_CODE) || (KERNEL_VERSION(5, 3, 0) < LINUX_VERSION_CODE) + ret = ytphy_config_init(phydev); +#else + ret = genphy_config_init(phydev); +#endif + if (ret < 0) + return ret; + + /* for utp to optimize signal */ + ret = ytphy_write_ext(phydev, 0x41, 0x33); + if (ret < 0) + return ret; + ret = ytphy_write_ext(phydev, 0x42, 0x66); + if (ret < 0) + return ret; + ret = ytphy_write_ext(phydev, 0x43, 0xaa); + if (ret < 0) + return ret; + ret = ytphy_write_ext(phydev, 0x44, 0xd0d); + if (ret < 0) + return ret; + +#if (YTPHY_861X_ABC_VER) + if ((port == 2) || (port == 5)) { + ret = ytphy_write_ext(phydev, 0x57, 0x2929); + if (ret < 0) + return ret; + } +#endif + + val = phy_read(phydev, MII_BMCR); + phy_write(phydev, MII_BMCR, val | BMCR_RESET); + do { + msleep(50); + ret = phy_read(phydev, MII_BMCR); + if (ret < 0) + return ret; + } while ((ret & BMCR_RESET) && --retries); + if (ret & BMCR_RESET) + return -ETIMEDOUT; + + /* for QSGMII optimization */ + ytphy_write_ext(phydev, 0xa000, 0x02); + + ret = ytphy_write_ext(phydev, 0x3, 0x4F80); + if (ret < 0) { + ytphy_write_ext(phydev, 0xa000, 0); + return ret; + } + ret = ytphy_write_ext(phydev, 0xe, 0x4F80); + if (ret < 0) { + ytphy_write_ext(phydev, 0xa000, 0); + return ret; + } + + ytphy_write_ext(phydev, 0xa000, 0); + +#if (KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE) + netdev_info(phydev->attached_dev, "%s done, phy addr: %d\n", __func__, phydev->addr); +#else + netdev_info(phydev->attached_dev, "%s done, phy addr: %d\n", __func__, phydev->mdio.addr); +#endif + return ret; +} + +static int yt8614_hw_strap_polling(struct phy_device *phydev) +{ + int val = 0; + + val = ytphy_read_ext(phydev, 0xa007) & 0xf; + switch (val) { + case 8: //4'b1000, Fiber x4 + Copper x4 + case 12: //4'b1100, QSGMII x1 + Combo x4 mode; + case 13: //4'b1101, QSGMII x1 + Combo x4 mode; + return (YT_PHY_MODE_FIBER | YT_PHY_MODE_UTP); + case 14: //4'b1110, QSGMII x1 + SGMII(MAC) x4 mode; + case 11: //4'b1011, QSGMII x1 + Fiber x4 mode; + return YT_PHY_MODE_FIBER; + case 9: //4'b1001, Reserved. + case 10: //4'b1010, QSGMII x1 + Copper x4 mode + case 15: //4'b1111, SGMII(PHY) x4 + Copper x4 mode + default: + return YT_PHY_MODE_UTP; + } +} + +#if (YTPHY_861X_ABC_VER) +static int yt8614_get_port_from_phydev(struct phy_device *phydev) +{ + int tmp = ytphy_read_ext(phydev, 0xa0ff); + int phy_addr = 0; + +#if (KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE) + phy_addr = (unsigned int)phydev->addr; +#else + phy_addr = (unsigned int)phydev->mdio.addr; +#endif + + if ((phy_addr - tmp) < 0) { + ytphy_write_ext(phydev, 0xa0ff, phy_addr); + tmp = phy_addr; + } + + return (phy_addr - tmp); +} +#endif + +static int yt8614_config_init(struct phy_device *phydev) +{ + int ret = 0; + int val, hw_strap_mode; + unsigned int retries = 12; +#if (YTPHY_861X_ABC_VER) + int port = 0; +#endif + phydev->irq = PHY_POLL; + + /* NOTE: this function should not be called more than one for each chip. */ + hw_strap_mode = ytphy_read_ext(phydev, 0xa007) & 0xf; + +#if (YTPHY_861X_ABC_VER) + port = yt8614_get_port_from_phydev(phydev); +#endif + + ytphy_write_ext(phydev, 0xa000, 0); + +#if (KERNEL_VERSION(4, 0, 0) > LINUX_VERSION_CODE) || (KERNEL_VERSION(5, 3, 0) < LINUX_VERSION_CODE) + ret = ytphy_config_init(phydev); +#else + ret = genphy_config_init(phydev); +#endif + if (ret < 0) + return ret; + + /* for utp to optimize signal */ + ret = ytphy_write_ext(phydev, 0x41, 0x33); + if (ret < 0) + return ret; + ret = ytphy_write_ext(phydev, 0x42, 0x66); + if (ret < 0) + return ret; + ret = ytphy_write_ext(phydev, 0x43, 0xaa); + if (ret < 0) + return ret; + ret = ytphy_write_ext(phydev, 0x44, 0xd0d); + if (ret < 0) + return ret; + +#if (YTPHY_861X_ABC_VER) + if (port == 2) { + ret = ytphy_write_ext(phydev, 0x57, 0x2929); + if (ret < 0) + return ret; + } +#endif + + /* soft reset to take config effect */ + val = phy_read(phydev, MII_BMCR); + phy_write(phydev, MII_BMCR, val | BMCR_RESET); + do { + msleep(50); + ret = phy_read(phydev, MII_BMCR); + if (ret < 0) + return ret; + } while ((ret & BMCR_RESET) && --retries); + if (ret & BMCR_RESET) + return -ETIMEDOUT; + + /* for QSGMII optimization */ + ytphy_write_ext(phydev, 0xa000, 0x02); + ret = ytphy_write_ext(phydev, 0x3, 0x4F80); + if (ret < 0) { + ytphy_write_ext(phydev, 0xa000, 0); + return ret; + } + ret = ytphy_write_ext(phydev, 0xe, 0x4F80); + if (ret < 0) { + ytphy_write_ext(phydev, 0xa000, 0); + return ret; + } + + /* for SGMII optimization */ + ytphy_write_ext(phydev, 0xa000, 0x03); + ret = ytphy_write_ext(phydev, 0x3, 0x2420); + if (ret < 0) { + ytphy_write_ext(phydev, 0xa000, 0); + return ret; + } + ret = ytphy_write_ext(phydev, 0xe, 0x24a0); + if (ret < 0) { + ytphy_write_ext(phydev, 0xa000, 0); + return ret; + } + + /* back up to utp*/ + ytphy_write_ext(phydev, 0xa000, 0); + +#if (KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE) + netdev_info(phydev->attached_dev, "%s done, phy addr: %d, chip mode: %d\n", __func__, phydev->addr, hw_strap_mode); +#else + netdev_info(phydev->attached_dev, "%s done, phy addr: %d, chip mode: %d\n", __func__, phydev->mdio.addr, hw_strap_mode); +#endif + return ret; +} + +int yt8618_aneg_done(struct phy_device *phydev) +{ +#if (KERNEL_VERSION(3, 14, 79) < LINUX_VERSION_CODE) + return genphy_aneg_done(phydev); +#else + return 1; +#endif +} + +int yt8614_aneg_done(struct phy_device *phydev) +{ + int link_fiber = 0, link_utp = 0; + + if (YT8614_PHY_MODE_CURR & YT_PHY_MODE_FIBER) { + /* reading Fiber */ + ytphy_write_ext(phydev, 0xa000, 3); + link_fiber = !!(phy_read(phydev, REG_PHY_SPEC_STATUS) & (BIT(YT8521_LINK_STATUS_BIT))); + } + + if (YT8614_PHY_MODE_CURR & YT_PHY_MODE_UTP) { + /* reading UTP */ + ytphy_write_ext(phydev, 0xa000, 0); + link_utp = !!(phy_read(phydev, REG_PHY_SPEC_STATUS) & (BIT(YT8521_LINK_STATUS_BIT))); + } + + return !!(link_fiber | link_utp); +} + +static int yt8614_read_status(struct phy_device *phydev) +{ + int ret; + int val, yt8614_fiber_latch_val, yt8614_fiber_curr_val; + int link; + int link_utp = 0, link_fiber = 0; + + if (YT8614_PHY_MODE_CURR & YT_PHY_MODE_UTP) { + /* switch to utp and reading regs */ + ret = ytphy_write_ext(phydev, 0xa000, 0); + if (ret < 0) + return ret; + + val = phy_read(phydev, REG_PHY_SPEC_STATUS); + if (val < 0) + return val; + + link = val & (BIT(YT8521_LINK_STATUS_BIT)); + if (link) { + link_utp = 1; + // here is same as 8521 and re-use the function; + yt8521_adjust_status(phydev, val, 1); + } else { + link_utp = 0; + } + } + + if (YT8614_PHY_MODE_CURR & YT_PHY_MODE_FIBER) { + /* reading Fiber/sgmii */ + ret = ytphy_write_ext(phydev, 0xa000, 3); + if (ret < 0) + return ret; + + val = phy_read(phydev, REG_PHY_SPEC_STATUS); + if (val < 0) + return val; + + /* for fiber, from 1000m to 100m, there is not link down from 0x11, + * and check reg 1 to identify such case + */ + yt8614_fiber_latch_val = phy_read(phydev, MII_BMSR); + yt8614_fiber_curr_val = phy_read(phydev, MII_BMSR); + link = val & (BIT(YT8521_LINK_STATUS_BIT)); + if (link && yt8614_fiber_latch_val != yt8614_fiber_curr_val) { + link = 0; +#if (KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE) + netdev_info(phydev->attached_dev, "%s, phy addr: %d, fiber link down detect, latch = %04x, curr = %04x\n", + __func__, phydev->addr, yt8614_fiber_latch_val, yt8614_fiber_curr_val); +#else + netdev_info(phydev->attached_dev, "%s, phy addr: %d, fiber link down detect, latch = %04x, curr = %04x\n", + __func__, phydev->mdio.addr, yt8614_fiber_latch_val, yt8614_fiber_curr_val); +#endif + } + + if (link) { + link_fiber = 1; + yt8521_adjust_status(phydev, val, 0); + } else { + link_fiber = 0; + } + } + + if (link_utp || link_fiber) { + if (phydev->link == 0) +#if (KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE) + netdev_info(phydev->attached_dev, "%s, phy addr: %d, link up, media %s\n", + __func__, phydev->addr, (link_utp && link_fiber) ? "both UTP and Fiber" : (link_utp ? "UTP" : "Fiber")); +#else + netdev_info(phydev->attached_dev, "%s, phy addr: %d, link up, media %s\n", + __func__, phydev->mdio.addr, (link_utp && link_fiber) ? "both UTP and Fiber" : (link_utp ? "UTP" : "Fiber")); +#endif + phydev->link = 1; + } else { + if (phydev->link == 1) +#if (KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE) + netdev_info(phydev->attached_dev, "%s, phy addr: %d, link down\n", __func__, phydev->addr); +#else + netdev_info(phydev->attached_dev, "%s, phy addr: %d, link down\n", __func__, phydev->mdio.addr); +#endif + phydev->link = 0; + } + + if (YT8614_PHY_MODE_CURR & YT_PHY_MODE_UTP) { + if (link_utp) + ytphy_write_ext(phydev, 0xa000, 0); + } + return 0; +} + +static int yt8618_read_status(struct phy_device *phydev) +{ + int ret; + /* maybe for 8614 yt8521_fiber_latch_val, yt8521_fiber_curr_val; */ + int val; + int link; + int link_utp = 0, link_fiber = 0; + + /* switch to utp and reading regs */ + ret = ytphy_write_ext(phydev, 0xa000, 0); + if (ret < 0) + return ret; + + val = phy_read(phydev, REG_PHY_SPEC_STATUS); + if (val < 0) + return val; + + link = val & (BIT(YT8521_LINK_STATUS_BIT)); + if (link) { + link_utp = 1; + yt8521_adjust_status(phydev, val, 1); + } else { + link_utp = 0; + } + + if (link_utp || link_fiber) + phydev->link = 1; + else + phydev->link = 0; + + return 0; +} + +int yt8618_suspend(struct phy_device *phydev) +{ +#if !(SYS_WAKEUP_BASED_ON_ETH_PKT) + int value; + +#if (KERNEL_VERSION(4, 0, 0) > LINUX_VERSION_CODE) + mutex_lock(&phydev->lock); +#else + /* no need lock in 4.19 */ +#endif + + ytphy_write_ext(phydev, 0xa000, 0); + value = phy_read(phydev, MII_BMCR); + phy_write(phydev, MII_BMCR, value | BMCR_PDOWN); + +#if (KERNEL_VERSION(4, 0, 0) > LINUX_VERSION_CODE) + mutex_unlock(&phydev->lock); +#else + /* no need lock/unlock in 4.19 */ +#endif +#endif /*!(SYS_WAKEUP_BASED_ON_ETH_PKT)*/ + + return 0; +} + +int yt8618_resume(struct phy_device *phydev) +{ +#if !(SYS_WAKEUP_BASED_ON_ETH_PKT) + int value; + +#if (KERNEL_VERSION(4, 0, 0) > LINUX_VERSION_CODE) + mutex_lock(&phydev->lock); +#else + /* no need lock/unlock in 4.19 */ +#endif + + ytphy_write_ext(phydev, 0xa000, 0); + value = phy_read(phydev, MII_BMCR); + phy_write(phydev, MII_BMCR, value & ~BMCR_PDOWN); + +#if (KERNEL_VERSION(4, 0, 0) > LINUX_VERSION_CODE) + mutex_unlock(&phydev->lock); +#else + /* no need lock/unlock in 4.19 */ +#endif +#endif /*!(SYS_WAKEUP_BASED_ON_ETH_PKT)*/ + + return 0; +} + +int yt8614_suspend(struct phy_device *phydev) +{ +#if !(SYS_WAKEUP_BASED_ON_ETH_PKT) + int value; + +#if (KERNEL_VERSION(4, 0, 0) > LINUX_VERSION_CODE) + mutex_lock(&phydev->lock); +#else + /* no need lock in 4.19 */ +#endif + + ytphy_write_ext(phydev, 0xa000, 0); + value = phy_read(phydev, MII_BMCR); + phy_write(phydev, MII_BMCR, value | BMCR_PDOWN); + + ytphy_write_ext(phydev, 0xa000, 3); + value = phy_read(phydev, MII_BMCR); + phy_write(phydev, MII_BMCR, value | BMCR_PDOWN); + + ytphy_write_ext(phydev, 0xa000, 0); + +#if (KERNEL_VERSION(4, 0, 0) > LINUX_VERSION_CODE) + mutex_unlock(&phydev->lock); +#else + /* no need lock/unlock in 4.19 */ +#endif +#endif /*!(SYS_WAKEUP_BASED_ON_ETH_PKT)*/ + + return 0; +} + +int yt8614_resume(struct phy_device *phydev) +{ +#if !(SYS_WAKEUP_BASED_ON_ETH_PKT) + int value; + +#if (KERNEL_VERSION(4, 0, 0) > LINUX_VERSION_CODE) + mutex_lock(&phydev->lock); +#else + /* no need lock/unlock in 4.19 */ +#endif + + ytphy_write_ext(phydev, 0xa000, 0); + value = phy_read(phydev, MII_BMCR); + phy_write(phydev, MII_BMCR, value & ~BMCR_PDOWN); + + ytphy_write_ext(phydev, 0xa000, 3); + value = phy_read(phydev, MII_BMCR); + phy_write(phydev, MII_BMCR, value & ~BMCR_PDOWN); + + ytphy_write_ext(phydev, 0xa000, 0); + +#if (KERNEL_VERSION(4, 0, 0) > LINUX_VERSION_CODE) + mutex_unlock(&phydev->lock); +#else + /* no need lock/unlock in 4.19 */ +#endif +#endif /* !(SYS_WAKEUP_BASED_ON_ETH_PKT) */ + + return 0; +} + +#if (KERNEL_VERSION(4, 0, 0) > LINUX_VERSION_CODE) +#else +int yt8821_soft_reset(struct phy_device *phydev) +{ + int ret, val; + + val = ytphy_read_ext(phydev, 0xa001); + ytphy_write_ext(phydev, 0xa001, (val & ~0x8000)); + + ytphy_write_ext(phydev, 0xa000, 0); + ret = ytphy_soft_reset(phydev); + + return ret; +} +#endif + +static int yt8821_init(struct phy_device *phydev) +{ + int ret = 0; + int val = 0; + + /* sds pll cfg */ + ret = ytphy_write_ext(phydev, 0xa050, 0x1000); + if (ret < 0) + return ret; + ret = ytphy_write_ext(phydev, 0xa000, 0x2); + if (ret < 0) + return ret; + ret = ytphy_write_ext(phydev, 0x23, 0x47a1); + if (ret < 0) + return ret; + ret = ytphy_write_ext(phydev, 0xbd, 0x3547); + if (ret < 0) + return ret; + + /* wait 1s */ + msleep(1000); + + /* calibration dcc */ + ret = ytphy_write_ext(phydev, 0xbd, 0xa547); + if (ret < 0) + return ret; + ret = ytphy_write_ext(phydev, 0x29, 0x3003); + if (ret < 0) + return ret; + + /* sds driver swing */ + ret = ytphy_write_ext(phydev, 0x25, 0x788); + if (ret < 0) + return ret; + + /* phy cfg */ + ret = ytphy_write_ext(phydev, 0xa000, 0x0); + if (ret < 0) + return ret; + + /* phy template cfg */ + ret = ytphy_write_ext(phydev, 0x471, 0x4545); + if (ret < 0) + return ret; + ret = ytphy_write_ext(phydev, 0x476, 0x4848); + if (ret < 0) + return ret; + ret = ytphy_write_ext(phydev, 0x477, 0x4848); + if (ret < 0) + return ret; + ret = ytphy_write_ext(phydev, 0x478, 0x4848); + if (ret < 0) + return ret; + ret = ytphy_write_ext(phydev, 0x479, 0x4848); + if (ret < 0) + return ret; + + /* calibrate phy lc pll */ + ret = ytphy_write_ext(phydev, 0x600, 0x2300); + if (ret < 0) + return ret; + ret = ytphy_write_ext(phydev, 0x8, 0x8041); + if (ret < 0) + return ret; + + /* prm_small_lng/med */ + ret = ytphy_write_ext(phydev, 0x388, 0x90); + if (ret < 0) + return ret; + ret = ytphy_write_ext(phydev, 0x387, 0x90); + if (ret < 0) + return ret; + + /* echo_delay_cfg */ + ret = ytphy_write_ext(phydev, 0x3, 0xa026); + if (ret < 0) + return ret; + + /* pbo setting */ + ret = ytphy_write_ext(phydev, 0x47e, 0x3535); + if (ret < 0) + return ret; + ret = ytphy_write_ext(phydev, 0x47f, 0x3535); + if (ret < 0) + return ret; + ret = ytphy_write_ext(phydev, 0x480, 0x3535); + if (ret < 0) + return ret; + ret = ytphy_write_ext(phydev, 0x481, 0x3535); + if (ret < 0) + return ret; + ret = ytphy_write_ext(phydev, 0x483, 0x2a2a); + if (ret < 0) + return ret; + ret = ytphy_write_ext(phydev, 0x484, 0x2a2a); + if (ret < 0) + return ret; + ret = ytphy_write_ext(phydev, 0x485, 0x2a2a); + if (ret < 0) + return ret; + ret = ytphy_write_ext(phydev, 0x486, 0x2a2a); + if (ret < 0) + return ret; + ret = ytphy_write_ext(phydev, 0x488, 0x2121); + if (ret < 0) + return ret; + ret = ytphy_write_ext(phydev, 0x489, 0x2121); + if (ret < 0) + return ret; + ret = ytphy_write_ext(phydev, 0x48a, 0x2121); + if (ret < 0) + return ret; + ret = ytphy_write_ext(phydev, 0x48b, 0x2121); + if (ret < 0) + return ret; + ret = ytphy_write_ext(phydev, 0x48d, 0x1a1a); + if (ret < 0) + return ret; + ret = ytphy_write_ext(phydev, 0x48e, 0x1a1a); + if (ret < 0) + return ret; + ret = ytphy_write_ext(phydev, 0x48f, 0x1a1a); + if (ret < 0) + return ret; + ret = ytphy_write_ext(phydev, 0x490, 0x1a1a); + if (ret < 0) + return ret; + ret = ytphy_write_ext(phydev, 0x492, 0x1515); + if (ret < 0) + return ret; + ret = ytphy_write_ext(phydev, 0x493, 0x1515); + if (ret < 0) + return ret; + ret = ytphy_write_ext(phydev, 0x494, 0x1515); + if (ret < 0) + return ret; + ret = ytphy_write_ext(phydev, 0x495, 0x1515); + if (ret < 0) + return ret; + ret = ytphy_write_ext(phydev, 0x497, 0x1111); + if (ret < 0) + return ret; + ret = ytphy_write_ext(phydev, 0x498, 0x1111); + if (ret < 0) + return ret; + ret = ytphy_write_ext(phydev, 0x499, 0x1111); + if (ret < 0) + return ret; + ret = ytphy_write_ext(phydev, 0x49a, 0x1111); + if (ret < 0) + return ret; + ret = ytphy_write_ext(phydev, 0x49c, 0x0d0d); + if (ret < 0) + return ret; + ret = ytphy_write_ext(phydev, 0x49d, 0x0d0d); + if (ret < 0) + return ret; + ret = ytphy_write_ext(phydev, 0x49e, 0x0d0d); + if (ret < 0) + return ret; + ret = ytphy_write_ext(phydev, 0x49f, 0x0d0d); + if (ret < 0) + return ret; + ret = ytphy_write_ext(phydev, 0xa052, 0x7); + if (ret < 0) + return ret; + + /* fast link down cfg */ + ret = ytphy_write_ext(phydev, 0x355, 0x7d07); + if (ret < 0) + return ret; + + /* soft reset */ + val = phy_read(phydev, MII_BMCR); + if (val < 0) + return val; + ret = phy_write(phydev, MII_BMCR, val | BMCR_RESET); + + return ret; +} + +static int yt8821_config_init(struct phy_device *phydev) +{ + int ret; + int val, hw_strap_mode; + + phydev->irq = PHY_POLL; + + /* NOTE: this function should not be called more than one for each chip. */ + hw_strap_mode = ytphy_read_ext(phydev, 0xa001) & 0x7; + + ytphy_write_ext(phydev, 0xa000, 0); +#if (KERNEL_VERSION(4, 0, 0) > LINUX_VERSION_CODE) || (KERNEL_VERSION(5, 3, 0) < LINUX_VERSION_CODE) + ret = ytphy_config_init(phydev); +#else + ret = genphy_config_init(phydev); +#endif + if (ret < 0) + return ret; + + ret = yt8821_init(phydev); + if (ret < 0) + return ret; + + /* disable auto sleep */ + val = ytphy_read_ext(phydev, YT8521_EXTREG_SLEEP_CONTROL1); + if (val < 0) + return val; + + val &= (~BIT(YT8521_EN_SLEEP_SW_BIT)); + + ret = ytphy_write_ext(phydev, YT8521_EXTREG_SLEEP_CONTROL1, val); + if (ret < 0) + return ret; + +#if (KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE) + netdev_info(phydev->attached_dev, "%s done, phy addr: %d, strap mode = %d\n", __func__, phydev->addr, hw_strap_mode); +#else + netdev_info(phydev->attached_dev, "%s done, phy addr: %d, strap mode = %d\n", __func__, phydev->mdio.addr, hw_strap_mode); +#endif + + return ret; +} + +/* for fiber mode, there is no 10M speed mode and + * this function is for this purpose. + */ +static int yt8821_adjust_status(struct phy_device *phydev, int val, int is_utp) +{ + int speed_mode, duplex; + int speed_mode_bit15_14, speed_mode_bit9; +#if (KERNEL_VERSION(4, 0, 0) > LINUX_VERSION_CODE) + int speed = -1; +#else + int speed = SPEED_UNKNOWN; +#endif + + if (is_utp) + duplex = (val & YT8512_DUPLEX) >> YT8521_DUPLEX_BIT; + else + duplex = 1; + + /* Bit9-Bit15-Bit14 speed mode 100---2.5G; 010---1000M; 001---100M; 000---10M */ + speed_mode_bit15_14 = (val & YT8521_SPEED_MODE) >> YT8521_SPEED_MODE_BIT; + speed_mode_bit9 = (val & BIT(9)) >> 9; + speed_mode = (speed_mode_bit9 << 2) | speed_mode_bit15_14; + switch (speed_mode) { + case 0: + if (is_utp) + speed = SPEED_10; + break; + case 1: + speed = SPEED_100; + break; + case 2: + speed = SPEED_1000; + break; + case 4: + speed = SPEED_2500; + break; + default: +#if (KERNEL_VERSION(4, 0, 0) > LINUX_VERSION_CODE) + speed = -1; +#else + speed = SPEED_UNKNOWN; +#endif + break; + } + + phydev->speed = speed; + phydev->duplex = duplex; + + return 0; +} + +static int yt8821_read_status(struct phy_device *phydev) +{ + int ret; + int val; + int yt8521_fiber_latch_val; + int yt8521_fiber_curr_val; + int link; + int link_utp = 0, link_fiber = 0; + + if (YT8521_PHY_MODE_CURR != YT8521_PHY_MODE_FIBER) { + /* reading UTP */ + ret = ytphy_write_ext(phydev, 0xa000, 0); + if (ret < 0) + return ret; + + val = phy_read(phydev, REG_PHY_SPEC_STATUS); + if (val < 0) + return val; + + link = val & (BIT(YT8521_LINK_STATUS_BIT)); + if (link) { + link_utp = 1; + yt8821_adjust_status(phydev, val, 1); /* speed(2500), duplex */ + } else { + link_utp = 0; + } + } //(YT8521_PHY_MODE_CURR != YT8521_PHY_MODE_FIBER) + + if (YT8521_PHY_MODE_CURR != YT8521_PHY_MODE_UTP) { + /* reading Fiber */ + ret = ytphy_write_ext(phydev, 0xa000, 2); + if (ret < 0) + return ret; + + val = phy_read(phydev, REG_PHY_SPEC_STATUS); + if (val < 0) + return val; + + //note: below debug information is used to check multiple PHy ports. + + /* for fiber, from 1000m to 100m, there is not link down from 0x11, + * and check reg 1 to identify such case this is important for Linux + * kernel for that, missing linkdown event will cause problem. + */ + yt8521_fiber_latch_val = phy_read(phydev, MII_BMSR); + yt8521_fiber_curr_val = phy_read(phydev, MII_BMSR); + link = val & (BIT(YT8521_LINK_STATUS_BIT)); + if (link && yt8521_fiber_latch_val != yt8521_fiber_curr_val) { + link = 0; +#if (KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE) + netdev_info(phydev->attached_dev, "%s, phy addr: %d, fiber link down detect, latch = %04x, curr = %04x\n", + __func__, phydev->addr, yt8521_fiber_latch_val, yt8521_fiber_curr_val); +#else + netdev_info(phydev->attached_dev, "%s, phy addr: %d, fiber link down detect, latch = %04x, curr = %04x\n", + __func__, phydev->mdio.addr, yt8521_fiber_latch_val, yt8521_fiber_curr_val); +#endif + } + + if (link) { + link_fiber = 1; + yt8821_adjust_status(phydev, val, 0); + } else { + link_fiber = 0; + } + } //(YT8521_PHY_MODE_CURR != YT8521_PHY_MODE_UTP) + + if (link_utp || link_fiber) { + if (phydev->link == 0) +#if (KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE) + netdev_info(phydev->attached_dev, "%s, phy addr: %d, link up, media: %s, mii reg 0x11 = 0x%x\n", + __func__, phydev->addr, (link_utp && link_fiber) ? "UNKNOWN MEDIA" : (link_utp ? "UTP" : "Fiber"), (unsigned int)val); +#else + netdev_info(phydev->attached_dev, "%s, phy addr: %d, link up, media: %s, mii reg 0x11 = 0x%x\n", + __func__, phydev->mdio.addr, (link_utp && link_fiber) ? "UNKNOWN MEDIA" : (link_utp ? "UTP" : "Fiber"), (unsigned int)val); +#endif + phydev->link = 1; + } else { + if (phydev->link == 1) +#if (KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE) + netdev_info(phydev->attached_dev, "%s, phy addr: %d, link down\n", __func__, phydev->addr); +#else + netdev_info(phydev->attached_dev, "%s, phy addr: %d, link down\n", __func__, phydev->mdio.addr); +#endif + + phydev->link = 0; + } + + if (YT8521_PHY_MODE_CURR != YT8521_PHY_MODE_FIBER) { + if (link_fiber) + ytphy_write_ext(phydev, 0xa000, 2); + if (link_utp) + ytphy_write_ext(phydev, 0xa000, 0); + } + return 0; +} + +#if (KERNEL_VERSION(5, 1, 21) < LINUX_VERSION_CODE) +static int yt8821_get_features(struct phy_device *phydev) +{ + linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported, 1); + return genphy_read_abilities(phydev); +} +#endif + +static struct phy_driver ytphy_drvs[] = { + { + .phy_id = PHY_ID_YT8010, + .name = "YT8010 Automotive Ethernet", + .phy_id_mask = MOTORCOMM_PHY_ID_MASK, + .features = PHY_BASIC_FEATURES, + .flags = PHY_POLL, +#if (KERNEL_VERSION(4, 0, 0) > LINUX_VERSION_CODE) +#else + .soft_reset = yt8010_soft_reset, +#endif + .config_aneg = yt8010_config_aneg, +#if (KERNEL_VERSION(3, 14, 79) < LINUX_VERSION_CODE) + .aneg_done = yt8010_aneg_done, +#endif +#if (KERNEL_VERSION(4, 0, 0) > LINUX_VERSION_CODE) || (KERNEL_VERSION(5, 3, 0) < LINUX_VERSION_CODE) + .config_init = ytphy_config_init, +#else + .config_init = genphy_config_init, +#endif + .read_status = yt8010_read_status, + }, { + .phy_id = PHY_ID_YT8010AS, + .name = "YT8010AS Automotive Ethernet", + .phy_id_mask = MOTORCOMM_PHY_ID_MASK, + .features = PHY_BASIC_FEATURES, + .flags = PHY_POLL, +#if (KERNEL_VERSION(4, 0, 0) > LINUX_VERSION_CODE) +#else + .soft_reset = yt8010AS_soft_reset, +#endif +#if (KERNEL_VERSION(3, 14, 79) < LINUX_VERSION_CODE) + .aneg_done = yt8010_aneg_done, +#endif + .config_init = yt8010AS_config_init, + .read_status = yt8010_read_status, + }, { + .phy_id = PHY_ID_YT8510, + .name = "YT8510 100/10Mb Ethernet", + .phy_id_mask = MOTORCOMM_PHY_ID_MASK, + .features = PHY_BASIC_FEATURES, + .flags = PHY_POLL, + .config_aneg = genphy_config_aneg, +#if (KERNEL_VERSION(4, 0, 0) > LINUX_VERSION_CODE) || (KERNEL_VERSION(5, 3, 0) < LINUX_VERSION_CODE) + .config_init = ytphy_config_init, +#else + .config_init = genphy_config_init, +#endif + .read_status = genphy_read_status, + }, { + .phy_id = PHY_ID_YT8511, + .name = "YT8511 Gigabit Ethernet", + .phy_id_mask = MOTORCOMM_PHY_ID_MASK, + .features = PHY_GBIT_FEATURES, + .flags = PHY_POLL, + .config_aneg = genphy_config_aneg, +#if GMAC_CLOCK_INPUT_NEEDED + .config_init = yt8511_config_init, +#else +#if (KERNEL_VERSION(4, 0, 0) > LINUX_VERSION_CODE) || (KERNEL_VERSION(5, 3, 0) < LINUX_VERSION_CODE) + .config_init = ytphy_config_init, +#else + .config_init = genphy_config_init, +#endif +#endif + .read_status = genphy_read_status, + .suspend = genphy_suspend, + .resume = genphy_resume, + }, { + .phy_id = PHY_ID_YT8512, + .name = "YT8512 Ethernet", + .phy_id_mask = MOTORCOMM_PHY_ID_MASK, + .features = PHY_BASIC_FEATURES, + .flags = PHY_POLL, + .config_aneg = genphy_config_aneg, + .config_init = yt8512_config_init, + .read_status = yt8512_read_status, + .suspend = genphy_suspend, + .resume = genphy_resume, + }, { + .phy_id = PHY_ID_YT8512B, + .name = "YT8512B Ethernet", + .phy_id_mask = MOTORCOMM_PHY_ID_MASK, + .features = PHY_BASIC_FEATURES, + .flags = PHY_POLL, + .config_aneg = genphy_config_aneg, + .config_init = yt8512_config_init, + .read_status = yt8512_read_status, + .suspend = genphy_suspend, + .resume = genphy_resume, + }, { + .phy_id = PHY_ID_YT8521, + .name = "YT8521 Ethernet", + .phy_id_mask = MOTORCOMM_PHY_ID_MASK, + .features = PHY_GBIT_FEATURES, + .flags = PHY_POLL, +#if (KERNEL_VERSION(4, 0, 0) > LINUX_VERSION_CODE) +#else + .soft_reset = yt8521_soft_reset, +#endif + .config_aneg = genphy_config_aneg, +#if (KERNEL_VERSION(3, 14, 79) < LINUX_VERSION_CODE) + .aneg_done = yt8521_aneg_done, +#endif + .config_init = yt8521_config_init, + .read_status = yt8521_read_status, + .suspend = yt8521_suspend, + .resume = yt8521_resume, +#if (YTPHY_WOL_FEATURE_ENABLE) + .get_wol = &ytphy_wol_feature_get, + .set_wol = &ytphy_wol_feature_set, +#endif + }, { + /* same as 8521 */ + .phy_id = PHY_ID_YT8531S, + .name = "YT8531S Ethernet", + .phy_id_mask = MOTORCOMM_PHY_ID_MASK, + .features = PHY_GBIT_FEATURES, + .flags = PHY_POLL, +#if (KERNEL_VERSION(4, 0, 0) > LINUX_VERSION_CODE) +#else + .soft_reset = yt8521_soft_reset, +#endif + .config_aneg = genphy_config_aneg, +#if (KERNEL_VERSION(3, 14, 79) < LINUX_VERSION_CODE) + .aneg_done = yt8521_aneg_done, +#endif + .config_init = yt8531S_config_init, + .read_status = yt8521_read_status, + .suspend = yt8521_suspend, + .resume = yt8521_resume, +#if (YTPHY_WOL_FEATURE_ENABLE) + .get_wol = &ytphy_wol_feature_get, + .set_wol = &ytphy_wol_feature_set, +#endif + }, { + /* same as 8511 */ + .phy_id = PHY_ID_YT8531, + .name = "YT8531 Gigabit Ethernet", + .phy_id_mask = MOTORCOMM_PHY_ID_MASK, + .features = PHY_GBIT_FEATURES, + .flags = PHY_POLL, + .config_aneg = genphy_config_aneg, + + .config_init = yt8531_config_init, + .read_status = genphy_read_status, + .suspend = genphy_suspend, + .resume = genphy_resume, +#if (YTPHY_WOL_FEATURE_ENABLE) + .get_wol = &ytphy_wol_feature_get, + .set_wol = &ytphy_wol_feature_set, +#endif + }, { + .phy_id = PHY_ID_YT8618, + .name = "YT8618 Ethernet", + .phy_id_mask = MOTORCOMM_MPHY_ID_MASK, + .features = PHY_GBIT_FEATURES, + .flags = PHY_POLL, +#if (KERNEL_VERSION(4, 0, 0) > LINUX_VERSION_CODE) +#else + .soft_reset = yt8618_soft_reset, +#endif + .config_aneg = genphy_config_aneg, +#if (KERNEL_VERSION(3, 14, 79) < LINUX_VERSION_CODE) + .aneg_done = yt8618_aneg_done, +#endif + .config_init = yt8618_config_init, + .read_status = yt8618_read_status, + .suspend = yt8618_suspend, + .resume = yt8618_resume, + }, + { + .phy_id = PHY_ID_YT8614, + .name = "YT8614 Ethernet", + .phy_id_mask = MOTORCOMM_MPHY_ID_MASK_8614, + .features = PHY_GBIT_FEATURES, + .flags = PHY_POLL, +#if (KERNEL_VERSION(4, 0, 0) > LINUX_VERSION_CODE) +#else + .soft_reset = yt8614_soft_reset, +#endif + .config_aneg = genphy_config_aneg, +#if (KERNEL_VERSION(3, 14, 79) < LINUX_VERSION_CODE) + .aneg_done = yt8614_aneg_done, +#endif + .config_init = yt8614_config_init, + .read_status = yt8614_read_status, + .suspend = yt8614_suspend, + .resume = yt8614_resume, + }, + { + .phy_id = PHY_ID_YT8821, + .name = "YT8821 2.5Gb Ethernet", + .phy_id_mask = MOTORCOMM_PHY_ID_MASK_8821, +#if (KERNEL_VERSION(5, 2, 0) > LINUX_VERSION_CODE) + .features = PHY_GBIT_FEATURES, +#endif + .flags = PHY_POLL, +#if (KERNEL_VERSION(4, 0, 0) > LINUX_VERSION_CODE) +#else + .soft_reset = yt8821_soft_reset, +#endif + .config_aneg = genphy_config_aneg, +#if (KERNEL_VERSION(3, 14, 79) < LINUX_VERSION_CODE) + .aneg_done = yt8521_aneg_done, +#endif +#if (KERNEL_VERSION(5, 1, 21) < LINUX_VERSION_CODE) + .get_features = yt8821_get_features, +#endif + .config_init = yt8821_config_init, + .read_status = yt8821_read_status, + .suspend = yt8521_suspend, + .resume = yt8521_resume, + }, +}; + +#if (KERNEL_VERSION(4, 0, 0) > LINUX_VERSION_CODE) +static int ytphy_drivers_register(struct phy_driver *phy_drvs, int size) +{ + int i, j; + int ret; + + for (i = 0; i < size; i++) { + ret = phy_driver_register(&phy_drvs[i]); + if (ret) + goto err; + } + + return 0; + +err: + for (j = 0; j < i; j++) + phy_driver_unregister(&phy_drvs[j]); + + return ret; +} + +static void ytphy_drivers_unregister(struct phy_driver *phy_drvs, int size) +{ + int i; + + for (i = 0; i < size; i++) + phy_driver_unregister(&phy_drvs[i]); +} + +static int __init ytphy_init(void) +{ + return ytphy_drivers_register(ytphy_drvs, ARRAY_SIZE(ytphy_drvs)); +} + +static void __exit ytphy_exit(void) +{ + ytphy_drivers_unregister(ytphy_drvs, ARRAY_SIZE(ytphy_drvs)); +} + +module_init(ytphy_init); +module_exit(ytphy_exit); +#else +/* for linux 4.x */ +module_phy_driver(ytphy_drvs); +#endif + +MODULE_DESCRIPTION("Motorcomm PHY driver"); +MODULE_AUTHOR("Leilei Zhao"); +MODULE_LICENSE("GPL"); + +static struct mdio_device_id __maybe_unused motorcomm_tbl[] = { + { PHY_ID_YT8010, MOTORCOMM_PHY_ID_MASK }, + { PHY_ID_YT8510, MOTORCOMM_PHY_ID_MASK }, + { PHY_ID_YT8511, MOTORCOMM_PHY_ID_MASK }, + { PHY_ID_YT8512, MOTORCOMM_PHY_ID_MASK }, + { PHY_ID_YT8512B, MOTORCOMM_PHY_ID_MASK }, + { PHY_ID_YT8521, MOTORCOMM_PHY_ID_MASK }, + { PHY_ID_YT8531S, MOTORCOMM_PHY_ID_8531_MASK }, + { PHY_ID_YT8531, MOTORCOMM_PHY_ID_8531_MASK }, + { PHY_ID_YT8618, MOTORCOMM_MPHY_ID_MASK }, + { PHY_ID_YT8614, MOTORCOMM_MPHY_ID_MASK_8614 }, + { PHY_ID_YT8821, MOTORCOMM_PHY_ID_MASK_8821 }, + { } +}; + +MODULE_DEVICE_TABLE(mdio, motorcomm_tbl); + diff --git a/target/linux/phytium/files-5.10/drivers/pwm/pwm-phytium.c b/target/linux/phytium/files-5.10/drivers/pwm/pwm-phytium.c new file mode 100644 index 000000000..d4cc94dde --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/pwm/pwm-phytium.c @@ -0,0 +1,568 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Phytium PWM driver + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define REG_TCNT 0x00 +#define REG_TCTRL 0x04 +#define REG_STAT 0x08 + +#define REG_TPERIOD 0x0c +#define REG_PWMCTRL 0x10 +#define REG_PWMCCR 0x14 + +#define TCTRL_DIV_MASK 0x1ff8 +#define TCTRL_PWMMOD_MASK 0x4 +#define TCTRL_CAPMOD_MASK 0x3 +#define PWM_PERIOD_MASK 0xffff +#define PWM_DUTY_MASK 0xffff +#define PWM_MODE_MASK 0x4 +#define PWM_CTRL_INIT 0xc4 + +#define PWM_NUM 2 + +#define REG_DBCTRL 0x00 +#define REG_DBCLY 0x04 +#define PWM_UPDBCLY_MASK 0x3ff +#define PWM_DWDBCLY_MASK 0xffc00 +#define PWM_DB_POLARITY_MASK 0xc + +#define PWM_N(x) ((0x400)*(x)) +#define MAX_PARAMETER 2 + +struct phytium_pwm_state { + int rst; + int cntmod; + int dutymod; + unsigned int div; + int db_rst; + unsigned int updbcly; + unsigned int dwdbcly; + unsigned int dbpolarity; +}; + +struct phytium_pwm_param { + int cntmod; + int dutymod; + unsigned int div; + unsigned int updbcly; + unsigned int dwdbcly; + unsigned int dbpolarity; +}; + +struct phytium_pwm_variant { + u8 rst_mask; + u8 div; + int counter_mode; + int periodns; + int duty_ns; + int pwm_mode; + u8 duty_mode; + int updbcly; + int dwdbcly; +}; + +struct phytium_pwm_channel { + u32 period_ns; + u32 duty_ns; + u32 tin_ns; +}; + +struct phytium_pwm_chip { + struct pwm_chip chip; + struct pwm_state state_pm[PWM_NUM]; + struct phytium_pwm_variant variant; + struct phytium_pwm_state state; + u8 inverter_mask; + u8 disabled_mask; + int db_init; + void __iomem *base; + void __iomem *base1; + struct phytium_pwm_param parameter[MAX_PARAMETER]; + unsigned int num_parameters; + + unsigned long clk_rate; + struct clk *base_clk; +}; + +static inline struct phytium_pwm_chip *to_phytium_pwm_chip(struct pwm_chip *chip) +{ + return container_of(chip, struct phytium_pwm_chip, chip); +} + + +static void pwm_phytium_free(struct pwm_chip *chip, struct pwm_device *pwm) +{ + devm_kfree(chip->dev, pwm_get_chip_data(pwm)); + pwm_set_chip_data(pwm, NULL); +} + +static int pwm_phytium_enable(struct pwm_chip *chip, struct pwm_device *pwm, int n) +{ + struct phytium_pwm_chip *our_chip = to_phytium_pwm_chip(chip); + u32 reg; + + reg = readl(our_chip->base + PWM_N(n) + REG_TCTRL); + reg |= 0x2; + our_chip->state_pm[n].enabled = 1; + writel(reg, our_chip->base + PWM_N(n) + REG_TCTRL); + + return 0; +} + +static void pwm_phytium_disable(struct pwm_chip *chip, struct pwm_device *pwm, int n) +{ + struct phytium_pwm_chip *our_chip = to_phytium_pwm_chip(chip); + u32 reg; + + reg = readl(our_chip->base + PWM_N(n) + REG_TCTRL); + reg &= 0xfffffffd; + our_chip->state_pm[n].enabled = 0; + writel(reg, our_chip->base + PWM_N(n) + REG_TCTRL); +} + +static void pwm_phytium_dutymod(struct pwm_chip *chip, int dutymod, int n) +{ + struct phytium_pwm_chip *our_chip = to_phytium_pwm_chip(chip); + u32 reg; + + reg = readl(our_chip->base + PWM_N(n) + REG_PWMCTRL); + + if (dutymod == 0) + reg &= 0xfffffeff; + else if (dutymod == 1) + reg |= 0x100; + + writel(reg, our_chip->base + PWM_N(n) + REG_PWMCTRL); +} + +static void pwm_phytium_set_div(struct pwm_chip *chip, unsigned int div, int n) +{ + struct phytium_pwm_chip *our_chip = to_phytium_pwm_chip(chip); + u32 reg; + + reg = readl(our_chip->base + PWM_N(n) + REG_TCTRL); + reg &= 0xffff; + reg |= (div<<16); + writel(reg, our_chip->base + PWM_N(n) + REG_TCTRL); +} + +static void pwm_phytium_set_tmode(struct pwm_chip *chip, int tmode, int n) +{ + struct phytium_pwm_chip *our_chip = to_phytium_pwm_chip(chip); + u32 reg; + + reg = readl(our_chip->base + PWM_N(n) + REG_TCTRL); + if (tmode == 0) + reg &= 0xfffffffb; + else if (tmode == 1) + reg |= 0x4; + + writel(reg, our_chip->base + PWM_N(n) + REG_TCTRL); +} + +static void pwm_phytium_set_periodns(struct pwm_chip *chip, unsigned int periodns, int n) +{ + struct phytium_pwm_chip *our_chip = to_phytium_pwm_chip(chip); + u32 reg; + int div = our_chip->state.div; + u64 cycles; + + cycles = our_chip->clk_rate; + cycles *= (periodns / (div + 1)); + do_div(cycles, NSEC_PER_SEC); + + reg = readl(our_chip->base + PWM_N(n) + REG_TPERIOD); + cycles = (cycles & PWM_PERIOD_MASK) - 0x1; + our_chip->state_pm[n].period = cycles; + + writel(cycles, our_chip->base + PWM_N(n) + REG_TPERIOD); +} + +static void pwm_phytium_set_duty(struct pwm_chip *chip, unsigned int duty, int n) +{ + struct phytium_pwm_chip *our_chip = to_phytium_pwm_chip(chip); + u32 reg; + int div = our_chip->state.div; + u64 cycles; + + cycles = our_chip->clk_rate; + cycles *= (duty / (div + 1)); + do_div(cycles, NSEC_PER_SEC); + + reg = readl(our_chip->base + PWM_N(n) + REG_PWMCCR); + cycles = (cycles & PWM_DUTY_MASK) - 0x1; + our_chip->state_pm[n].duty_cycle = cycles; + + writel(cycles, our_chip->base + PWM_N(n) + REG_PWMCCR); +} + +static int pwm_phytium_set_dbcly(struct pwm_chip *chip, unsigned int updbcly, unsigned int dwdbcly) +{ + struct phytium_pwm_chip *our_chip = to_phytium_pwm_chip(chip); + u32 reg; + u64 dbcly, cycles, upcycles, dwcycles; + + reg = readl(our_chip->base + REG_TPERIOD); + cycles = our_chip->clk_rate; + dbcly &= 0x0; + if (updbcly) { + upcycles = cycles * updbcly; + do_div(upcycles, NSEC_PER_SEC); + + if (upcycles < reg) + dbcly |= (upcycles & PWM_UPDBCLY_MASK); + else + return -EINVAL; + } + + if (dwdbcly) { + dwcycles = cycles * dwdbcly; + do_div(dwcycles, NSEC_PER_SEC); + + if (dwcycles < reg) + dbcly |= ((dwcycles << 10) & PWM_DWDBCLY_MASK); + else + return -EINVAL; + } + + writel(dbcly, our_chip->base1 + REG_DBCLY); + + reg = readl(our_chip->base1 + REG_DBCTRL); + reg |= 0x30; + writel(reg, our_chip->base1 + REG_DBCTRL); + + return 0; +} + +static void pwm_phytium_set_dbpolarity(struct pwm_chip *chip, unsigned int db_polarity) +{ + struct phytium_pwm_chip *our_chip = to_phytium_pwm_chip(chip); + u32 reg; + + reg = readl(our_chip->base1 + REG_DBCTRL); + reg &= 0x33; + reg |= ((db_polarity<<2) & PWM_DB_POLARITY_MASK); + writel(reg, our_chip->base1 + REG_DBCTRL); +} + +static int pwm_phytium_init(struct pwm_chip *chip, struct pwm_device *pwm, int n) +{ + struct phytium_pwm_chip *our_chip = to_phytium_pwm_chip(chip); + + writel(PWM_CTRL_INIT, our_chip->base + PWM_N(n) + REG_PWMCTRL); + + pwm_phytium_dutymod(chip, our_chip->state.dutymod, n); + pwm_phytium_set_div(chip, our_chip->state.div, n); + pwm_phytium_set_tmode(chip, our_chip->state.cntmod, n); + + return 0; +} + +static int pwm_phytium_db_init(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct phytium_pwm_chip *our_chip = to_phytium_pwm_chip(chip); + + pwm_phytium_set_dbcly(chip, our_chip->state.updbcly, our_chip->state.dwdbcly); + pwm_phytium_set_dbpolarity(chip, our_chip->state.dbpolarity); + + return 0; +} + +static int __pwm_phytium_config(struct pwm_chip *chip, struct pwm_device *pwm) +{ + pwm_phytium_init(chip, pwm, 0); + pwm_phytium_init(chip, pwm, 1); + return 0; +} + +static int pwm_phytium_set_polarity(struct pwm_chip *chip, enum pwm_polarity polarity, int n) +{ + struct phytium_pwm_chip *our_chip = to_phytium_pwm_chip(chip); + u32 value; + + value = readl(our_chip->base + PWM_N(n) + REG_PWMCTRL); + + if (polarity == PWM_POLARITY_INVERSED) { + value &= 0xffffff0f; + value |= 0x30; + } else if (polarity == PWM_POLARITY_NORMAL) { + value &= 0xffffff0f; + value |= 0x40; + } + + our_chip->state_pm[n].polarity = polarity; + writel(value, our_chip->base + PWM_N(n) + REG_PWMCTRL); + + return 0; +} + +static int pwm_phytium_apply(struct pwm_chip *chip, struct pwm_device *pwm, + const struct pwm_state *state) +{ + struct phytium_pwm_chip *phytium_pwm = to_phytium_pwm_chip(chip); + struct pwm_state cstate; + u32 reg; + int n; + + pwm_get_state(pwm, &cstate); + + n = pwm->hwpwm & BIT(0); + + if ((state->polarity != cstate.polarity) && !state->enabled) + pwm_phytium_set_polarity(chip, state->polarity, n); + + if (state->enabled && !cstate.enabled) + pwm_phytium_enable(chip, pwm, n); + + if (!state->enabled && cstate.enabled) + pwm_phytium_disable(chip, pwm, n); + + if (state->period != cstate.period) { + pwm_phytium_set_periodns(chip, state->period, n); + if ((phytium_pwm->db_init == 1) && (n == 0)) + pwm_phytium_db_init(chip, pwm); + } + + if (state->duty_cycle != cstate.duty_cycle) { + if (phytium_pwm->state.dutymod == true) { + reg = readl(phytium_pwm->base + PWM_N(n) + REG_STAT); + if ((reg & 0x8) != 0x8) + pwm_phytium_set_duty(chip, state->duty_cycle, n); + } else { + pwm_phytium_set_duty(chip, state->duty_cycle, n); + } + } + + return 0; +} + +static int pwm_phytium_request(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct phytium_pwm_chip *our_chip = to_phytium_pwm_chip(chip); + struct phytium_pwm_channel *our_chan; + + our_chan = devm_kzalloc(chip->dev, sizeof(*our_chan), GFP_KERNEL); + if (!our_chan) + return -ENOMEM; + + pwm_set_chip_data(pwm, our_chan); + __pwm_phytium_config(&our_chip->chip, our_chip->chip.pwms); + + return 0; +} + +static const struct pwm_ops pwm_phytium_ops = { + .request = pwm_phytium_request, + .free = pwm_phytium_free, + .apply = pwm_phytium_apply, + .owner = THIS_MODULE, +}; + +static int phytium_pwm_set_parameter(struct phytium_pwm_chip *priv) +{ + unsigned int i; + + for (i = 0; i < priv->num_parameters; i++) { + if (priv->parameter[i].updbcly > 0 || priv->parameter[i].dwdbcly > 0) { + priv->db_init = 1; + priv->state.db_rst = 1; + } + + priv->state.cntmod = priv->parameter[i].cntmod; + priv->state.dutymod = priv->parameter[i].dutymod; + priv->state.div = priv->parameter[i].div; + priv->state.updbcly = priv->parameter[i].updbcly; + priv->state.dwdbcly = priv->parameter[i].dwdbcly; + priv->state.dbpolarity = priv->parameter[i].dbpolarity; + } + priv->state.rst = 1; + + return 0; +} + +static int pwm_phytium_probe_parameter(struct phytium_pwm_chip *priv, + struct fwnode_handle *np) +{ + int nb, ret, array_size; + unsigned int i; + + array_size = fwnode_property_read_u32_array(np, "phytium,db", NULL, 0); + nb = array_size / (sizeof(struct phytium_pwm_param) / sizeof(u32)); + if (nb <= 0 || nb > MAX_PARAMETER) + return -EINVAL; + + priv->num_parameters = nb; + ret = fwnode_property_read_u32_array(np, "phytium,db", + (u32 *)priv->parameter, array_size); + if (ret) + return ret; + + for (i = 0; i < priv->num_parameters; i++) { + if (priv->parameter[i].cntmod > 1 || + priv->parameter[i].dutymod > 1 || + priv->parameter[i].div > 4096 || + priv->parameter[i].dbpolarity > 3) + return -EINVAL; + } + + return phytium_pwm_set_parameter(priv); +} +static int pwm_phytium_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct fwnode_handle *np = dev_fwnode(dev); + struct phytium_pwm_chip *chip; + struct resource *res; + int ret; + + chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); + + if (chip == NULL) + return -ENOMEM; + + chip->chip.dev = &pdev->dev; + chip->chip.ops = &pwm_phytium_ops; + chip->chip.base = -1; + chip->chip.npwm = PWM_NUM; + chip->inverter_mask = BIT(PWM_NUM) - 1; + + if (dev->of_node) { + chip->chip.of_xlate = of_pwm_xlate_with_flags; + chip->chip.of_pwm_n_cells = 3; + } + ret = pwm_phytium_probe_parameter(chip, np); + if (ret) { + dev_err(dev, "failed to set parameter\n"); + return ret; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + chip->base1 = devm_ioremap_resource(&pdev->dev, res); + chip->base = (chip->base1 + 0x400); + + if (IS_ERR(chip->base)) { + dev_err(dev, "failed to get base_addr\n"); + return PTR_ERR(chip->base); + } + if (dev->of_node) { + chip->base_clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(chip->base_clk)) { + dev_err(dev, "failed to get clk\n"); + return PTR_ERR(chip->base_clk); + } + + ret = clk_prepare_enable(chip->base_clk); + if (ret < 0) { + dev_err(dev, "failed to enable clk\n"); + return ret; + } + chip->clk_rate = clk_get_rate(chip->base_clk); + } else if (has_acpi_companion(dev)){ + if(fwnode_property_read_u32(dev_fwnode(dev),"clock-frequency", (u32 *)&(chip->clk_rate) ) <0) + chip->clk_rate = 50000000; + } + platform_set_drvdata(pdev, chip); + + ret = pwmchip_add(&chip->chip); + + if (ret < 0) { + dev_err(dev, "failed to register PWM chip\n"); + return ret; + } + + return 0; +} + +static int pwm_phytium_remove(struct platform_device *pdev) +{ + struct phytium_pwm_chip *chip = platform_get_drvdata(pdev); + + pwmchip_remove(&chip->chip); + + clk_disable_unprepare(chip->base_clk); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int pwm_phytium_pm_init(struct phytium_pwm_chip *priv) +{ + int i; + + __pwm_phytium_config(&priv->chip, priv->chip.pwms); + for (i = 0; i < priv->chip.npwm; i++) { + writel(priv->state_pm[i].period, priv->base + PWM_N(i) + REG_TPERIOD); + if ((priv->db_init == 1) && (i == 0)) + pwm_phytium_db_init(&priv->chip, priv->chip.pwms); + writel(priv->state_pm[i].duty_cycle, priv->base + PWM_N(i) + REG_PWMCTRL); + pwm_phytium_set_polarity(&priv->chip, priv->state_pm[i].polarity, i); + if (priv->state_pm[i].enabled) + pwm_phytium_enable(&priv->chip, priv->chip.pwms, i); + } + + return 0; +} + +static int pwm_phytium_suspend(struct device *dev) +{ + return 0; +} + +static int pwm_phytium_resume(struct device *dev) +{ + struct phytium_pwm_chip *priv = dev_get_drvdata(dev); + + pwm_phytium_pm_init(priv); + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(phytium_pwm_dev_pm_ops, pwm_phytium_suspend, pwm_phytium_resume); + +#ifdef CONFIG_ACPI +static const struct acpi_device_id phytium_pwm_acpi_ids[] = { + { "PHYT0029", 0 }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(acpi, phytium_pwm_acpi_ids); +#endif + +static const struct of_device_id phytium_pwm_matches[] = { + { .compatible = "phytium,pwm" }, + {}, +}; +MODULE_DEVICE_TABLE(of, phytium_pwm_matches); + +static struct platform_driver pwm_phytium_driver = { + .driver = { + .name = "phytium-pwm", + .pm = &phytium_pwm_dev_pm_ops, + .of_match_table = phytium_pwm_matches, + .acpi_match_table = ACPI_PTR(phytium_pwm_acpi_ids), + }, + .probe = pwm_phytium_probe, + .remove = pwm_phytium_remove, +}; +module_platform_driver(pwm_phytium_driver); + +MODULE_DESCRIPTION("Phytium SoC PWM driver"); +MODULE_AUTHOR("Yang Liu "); +MODULE_LICENSE("GPL v2"); diff --git a/target/linux/phytium/files-5.10/drivers/remoteproc/homo_remoteproc.c b/target/linux/phytium/files-5.10/drivers/remoteproc/homo_remoteproc.c new file mode 100644 index 000000000..056cabdd7 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/remoteproc/homo_remoteproc.c @@ -0,0 +1,349 @@ +/* + * Homogeneous Remote Processor Control Driver + * + * Copyright (c) 2022-2023 Phytium Technology Co., Ltd. + * Author: Shaojun Yang + * + * 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 "remoteproc_internal.h" + +#define RPROC_RESOURCE_ENTRIES 8 + +#define PSCI_VERSION 0x84000000 +#define CPU_SUSPEND 0xc4000001 +#define CPU_OFF 0x84000002 +#define CPU_ON 0xc4000003 +#define AFFINITY_INFO 0xc4000004 +#define MIGRATE 0xc4000005 + +/* Resource table for the homo remote processors */ +struct homo_resource_table { + unsigned int version; + unsigned int num; + unsigned int reserved[2]; + unsigned int offset[RPROC_RESOURCE_ENTRIES]; + + /* Note: linux kenrel 'struct fw_rsc_vdev' has no 'type' field, here add to align data structre */ + uint32_t type; + /* rpmsg vdev entry */ + struct fw_rsc_vdev rpmsg_vdev; + struct fw_rsc_vdev_vring rpmsg_vring0; + struct fw_rsc_vdev_vring rpmsg_vring1; +}; + +struct homo_rproc { + struct rproc *rproc; + struct homo_resource_table *rsc; + + u64 phys_addr; + void *addr; + u64 size; + + int irq; + int cpu; +}; + +static int homo_rproc_irq; +static struct homo_rproc *g_priv; +static struct work_struct workqueue; + +#define MPIDR_TO_SGI_AFFINITY(cluster_id, level) (MPIDR_AFFINITY_LEVEL(cluster_id, level) << ICC_SGI1R_AFFINITY_## level ## _SHIFT) + +void gicv3_ipi_send_single(int irq, u64 mpidr) +{ + u16 tlist = 0; + u64 cluster_id; + u64 sgi1r; + + /* Ensure stores to Normal memory are visible to other CPUs before sending the IPI. */ + wmb(); + + cluster_id = mpidr & ~0xffUL; + tlist |= 1 << (mpidr & 0xf); + + /* Send the IPIs for the target list of this cluster */ + sgi1r = (MPIDR_TO_SGI_AFFINITY(cluster_id, 3) | + MPIDR_TO_SGI_AFFINITY(cluster_id, 2) | + irq << 24 | + MPIDR_TO_SGI_AFFINITY(cluster_id, 1) | tlist); + gic_write_sgi1r(sgi1r); + + /* Force the above writes to ICC_SGI1R_EL1 to be executed */ + isb(); +} + +static void homo_rproc_vq_irq(struct work_struct *work) +{ + struct homo_rproc *priv = g_priv; + struct homo_resource_table *rsc = priv->rsc; + struct rproc *rproc = priv->rproc; + + rproc_vq_interrupt(rproc, rsc->rpmsg_vring0.notifyid); +} + +static int homo_rproc_start(struct rproc *rproc) +{ + int err; + struct homo_rproc *priv = rproc->priv; + int phys_cpuid = cpu_logical_map(priv->cpu); + struct arm_smccc_res smc_res; + + err = psci_ops.affinity_info(phys_cpuid, 0); + if (err == 0) + remove_cpu(priv->cpu); + + INIT_WORK(&workqueue, homo_rproc_vq_irq); + + priv->rsc = (struct homo_resource_table *)rproc->table_ptr; + + /* ARMv8 requires to clean D-cache and invalidate I-cache for memory containing new instructions. */ + flush_icache_range((unsigned long)priv->addr, (unsigned long)(priv->addr + priv->size)); + + arm_smccc_smc(CPU_ON, phys_cpuid, (unsigned long long)priv->phys_addr, 0, 0, 0, 0, 0, &smc_res); + + return smc_res.a0; +} + +static int homo_rproc_stop(struct rproc *rproc) +{ + int err; + struct homo_rproc *priv = rproc->priv; + + err = psci_ops.affinity_info(cpu_logical_map(priv->cpu), 0); + if (err == 1) + add_cpu(priv->cpu); + + return 0; +} + +static void *homo_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len) +{ + struct homo_rproc *priv = rproc->priv; + + return priv->addr + (da - rproc->bootaddr); +} + +static void homo_rproc_kick(struct rproc *rproc, int vqid) +{ + struct homo_rproc *priv = rproc->priv; + + if (rproc->state == RPROC_RUNNING) + gicv3_ipi_send_single(priv->irq, cpu_logical_map(priv->cpu)); + + return ; +} + +static const struct rproc_ops homo_rproc_ops = { + .start = homo_rproc_start, + .stop = homo_rproc_stop, + .kick = homo_rproc_kick, + .da_to_va = homo_rproc_da_to_va, +}; + +static void __iomem *homo_ioremap_prot(phys_addr_t addr, size_t size, pgprot_t prot) +{ + unsigned long offset, vaddr; + phys_addr_t last_addr; + struct vm_struct *area; + + /* Disallow wrap-around or zero size */ + last_addr = addr + size - 1; + if (!size || last_addr < addr) + return NULL; + + /* Page-align mappings */ + offset = addr & (~PAGE_MASK); + addr -= offset; + size = PAGE_ALIGN(size + offset); + + area = get_vm_area_caller(size, VM_IOREMAP, + __builtin_return_address(0)); + if (!area) + return NULL; + vaddr = (unsigned long)area->addr; + + if (ioremap_page_range(vaddr, vaddr + size, addr, prot)) { + free_vm_area(area); + return NULL; + } + + return (void __iomem *)(vaddr + offset); +} + +static irqreturn_t homo_rproc_irq_handler(int irq, void *data) +{ + schedule_work(&workqueue); + return IRQ_HANDLED; +} + +static int homo_rproc_starting_cpu(unsigned int cpu) +{ + enable_percpu_irq(homo_rproc_irq, irq_get_trigger_type(homo_rproc_irq)); + return 0; +} + +static int homo_rproc_dying_cpu(unsigned int cpu) +{ + disable_percpu_irq(homo_rproc_irq); + return 0; +} + +static int homo_rproc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node, *p; + struct device_node *np_mem; + struct resource res; + struct rproc *rproc; + const char *fw_name; + struct homo_rproc *priv; + int ret; + unsigned int ipi, cpu; + struct of_phandle_args oirq; + + ret = rproc_of_parse_firmware(dev, 0, &fw_name); + if (ret) { + dev_err(dev, "failed to parse firmware-name property, ret = %d\n", ret); + return ret; + } + + rproc = rproc_alloc(dev, np->name, &homo_rproc_ops, fw_name, sizeof(*priv)); + if (!rproc) + return -ENOMEM; + + rproc->auto_boot = false; + rproc->has_iommu = false; + + platform_set_drvdata(pdev, rproc); + + priv = g_priv = rproc->priv; + priv->rproc = rproc; + + /* The following values can be modified through devicetree 'homo_rproc' node */ + if (of_property_read_u32(np, "remote-processor", &cpu)) { + dev_err(dev, "not found 'remote-processor' property\n"); + return -EINVAL; + } + + if (of_property_read_u32(np, "inter-processor-interrupt", &ipi)) { + dev_err(dev, "not found 'inter-processor-interrupt' property\n"); + return -EINVAL; + } + + /* The gic-v3 driver has registered the 0-7 range of SGI interrupt for system purpose */ + if (ipi < 8) { + dev_err(dev, "'inter-processor-interrupt' is %d, should be between 9~15\n", ipi); + return -EINVAL; + } + + priv->cpu = cpu; + priv->irq = ipi; + + dev_info(dev, "remote-processor = %d, inter-processor-interrupt = %d\n", cpu, ipi); + + np_mem = of_parse_phandle(np, "memory-region", 0); + ret = of_address_to_resource(np_mem, 0, &res); + if (ret) { + dev_err(dev, "can't find memory-region for Baremetal\n"); + return ret; + } + + priv->rsc = NULL; + priv->addr = NULL; + + priv->phys_addr = res.start; + priv->size = resource_size(&res); + + /* Map physical memory region reserved for homo remote processor. */ + priv->addr = homo_ioremap_prot(priv->phys_addr, priv->size, PAGE_KERNEL_EXEC); + if (!priv->addr) { + dev_err(dev, "ioremap failed\n"); + return -ENOMEM; + } + dev_info(dev, "ioremap: phys_addr = %016llx, addr = %llx, size = %lld\n", + priv->phys_addr, (u64)(priv->addr), priv->size); + + /* Look for the interrupt parent. */ + p = of_irq_find_parent(np); + if (p == NULL) { + ret = -EINVAL; + goto err; + } + + oirq.np = p; + oirq.args_count = 1; + oirq.args[0] = ipi; + homo_rproc_irq = irq_create_of_mapping(&oirq); + if (homo_rproc_irq <= 0) { + ret = -EINVAL; + goto err; + } + + ret = request_percpu_irq(homo_rproc_irq, homo_rproc_irq_handler, "homo-rproc-ipi", &cpu_number); + if (ret) { + dev_err(dev, "failed to request percpu irq, status = %d\n", ret); + goto err; + } + + ret = cpuhp_setup_state(CPUHP_AP_HOMO_RPROC_STARTING, "remoteproc/homo_rproc:starting", homo_rproc_starting_cpu, homo_rproc_dying_cpu); + if (ret) { + dev_err(dev, "cpuhp setup state failed, status = %d\n", ret); + goto err; + } + + ret = rproc_add(rproc); + if (ret) { + dev_err(dev, "failed to add register device with remoteproc core, status = %d\n", ret); + goto err; + } + + return 0; + +err: + vunmap((void *)((unsigned long)priv->addr & PAGE_MASK)); + return ret; +} + +static int homo_rproc_remove(struct platform_device *pdev) +{ + struct rproc *rproc = platform_get_drvdata(pdev); + + rproc_del(rproc); + rproc_free(rproc); + + return 0; +} + +static const struct of_device_id homo_rproc_ids[] = { + { .compatible = "homo,rproc", }, + { } +}; +MODULE_DEVICE_TABLE(of, homo_rproc_ids); + +static struct platform_driver homo_rproc_driver = { + .probe = homo_rproc_probe, + .remove = homo_rproc_remove, + .driver = { + .name = "homo-rproc", + .of_match_table = of_match_ptr(homo_rproc_ids), + }, +}; +module_platform_driver(homo_rproc_driver); + +MODULE_DESCRIPTION("Homogeneous Remote Processor Control Driver"); +MODULE_AUTHOR("Shaojun Yang "); +MODULE_LICENSE("GPL v2"); diff --git a/target/linux/phytium/files-5.10/drivers/rtc/rtc-sd3068.c b/target/linux/phytium/files-5.10/drivers/rtc/rtc-sd3068.c new file mode 100755 index 000000000..6abd0cd85 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/rtc/rtc-sd3068.c @@ -0,0 +1,255 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * rtc-sd3068.c - RTC driver for some mostly-compatible I2C chips. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SD3068_REG_SC 0x00 +#define SD3068_REG_MN 0x01 +#define SD3068_REG_HR 0x02 +#define SD3068_REG_DW 0x03 +#define SD3068_REG_DM 0x04 +#define SD3068_REG_MO 0x05 +#define SD3068_REG_YR 0x06 + +#define SD3068_REG_CTRL1 0x0f +#define SD3068_REG_CTRL2 0x10 +#define SD3068_REG_CTRL3 0x11 + +#define KEY_WRITE1 0x80 +#define KEY_WRITE2 0x04 +#define KEY_WRITE3 0x80 + +#define NUM_TIME_REGS (SD3068_REG_YR - SD3068_REG_SC + 1) + +/* + * The sd3068 has write protection + * and we can choose whether or not to use it. + * Write protection is turned off by default. + */ +#define WRITE_PROTECT_EN 1 + +struct sd3068 { + struct rtc_device *rtc; + struct regmap *regmap; +}; + +/* + * In order to prevent arbitrary modification of the time register, + * when modification of the register, + * the "write" bit needs to be written in a certain order. + * 1. set WRITE1 bit + * 2. set WRITE2 bit + * 3. set WRITE3 bit + */ +static void sd3068_enable_reg_write(struct sd3068 *sd3068) +{ + regmap_update_bits(sd3068->regmap, SD3068_REG_CTRL2, + KEY_WRITE1, KEY_WRITE1); + regmap_update_bits(sd3068->regmap, SD3068_REG_CTRL1, + KEY_WRITE2, KEY_WRITE2); + regmap_update_bits(sd3068->regmap, SD3068_REG_CTRL1, + KEY_WRITE3, KEY_WRITE3); +} + +#if WRITE_PROTECT_EN +/* + * In order to prevent arbitrary modification of the time register, + * we should disable the write function. + * when disable write, + * the "write" bit needs to be clear in a certain order. + * 1. clear WRITE2 bit + * 2. clear WRITE3 bit + * 3. clear WRITE1 bit + */ +static void sd3068_disable_reg_write(struct sd3068 *sd3068) +{ + regmap_update_bits(sd3068->regmap, SD3068_REG_CTRL1, + KEY_WRITE2, 0); + regmap_update_bits(sd3068->regmap, SD3068_REG_CTRL1, + KEY_WRITE3, 0); + regmap_update_bits(sd3068->regmap, SD3068_REG_CTRL2, + KEY_WRITE1, 0); +} +#endif + +static int sd3068_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + unsigned char hour; + unsigned char rtc_data[NUM_TIME_REGS] = {0}; + struct i2c_client *client = to_i2c_client(dev); + struct sd3068 *sd3068 = i2c_get_clientdata(client); + int ret; + pr_debug("sd3068 read\n"); + + ret = regmap_bulk_read(sd3068->regmap, SD3068_REG_SC, rtc_data, + NUM_TIME_REGS); + if (ret < 0) { + dev_err(dev, "reading from RTC failed with err:%d\n", ret); + return ret; + } + + tm->tm_sec = bcd2bin(rtc_data[SD3068_REG_SC] & 0x7F); + tm->tm_min = bcd2bin(rtc_data[SD3068_REG_MN] & 0x7F); + + /* + * The sd3068 supports 12/24 hour mode. + * When getting time, + * we need to convert the 12 hour mode to the 24 hour mode. + */ + hour = rtc_data[SD3068_REG_HR]; + if (hour & 0x80) /* 24H MODE */ + tm->tm_hour = bcd2bin(rtc_data[SD3068_REG_HR] & 0x3F); + else if (hour & 0x20) /* 12H MODE PM */ + tm->tm_hour = bcd2bin(rtc_data[SD3068_REG_HR] & 0x1F) + 12; + else /* 12H MODE AM */ + tm->tm_hour = bcd2bin(rtc_data[SD3068_REG_HR] & 0x1F); + + tm->tm_mday = bcd2bin(rtc_data[SD3068_REG_DM] & 0x3F); + tm->tm_wday = rtc_data[SD3068_REG_DW] & 0x07; + tm->tm_mon = bcd2bin(rtc_data[SD3068_REG_MO] & 0x1F) - 1; + tm->tm_year = bcd2bin(rtc_data[SD3068_REG_YR]) + 100; + + return 0; +} + +static int sd3068_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + unsigned char rtc_data[NUM_TIME_REGS]; + struct i2c_client *client = to_i2c_client(dev); + struct sd3068 *sd3068 = i2c_get_clientdata(client); + int ret; + pr_debug("sd3068 set\n"); + + rtc_data[SD3068_REG_SC] = bin2bcd(tm->tm_sec); + rtc_data[SD3068_REG_MN] = bin2bcd(tm->tm_min); + rtc_data[SD3068_REG_HR] = bin2bcd(tm->tm_hour) | 0x80; + rtc_data[SD3068_REG_DM] = bin2bcd(tm->tm_mday); + rtc_data[SD3068_REG_DW] = tm->tm_wday & 0x07; + rtc_data[SD3068_REG_MO] = bin2bcd(tm->tm_mon) + 1; + rtc_data[SD3068_REG_YR] = bin2bcd(tm->tm_year - 100); + +#if WRITE_PROTECT_EN + sd3068_enable_reg_write(sd3068); +#endif + + ret = regmap_bulk_write(sd3068->regmap, SD3068_REG_SC, rtc_data, + NUM_TIME_REGS); + if (ret < 0) { + dev_err(dev, "writing to RTC failed with err:%d\n", ret); + return ret; + } + +#if WRITE_PROTECT_EN + sd3068_disable_reg_write(sd3068); +#endif + + return 0; +} + +static const struct rtc_class_ops sd3068_rtc_ops = { + .read_time = sd3068_rtc_read_time, + .set_time = sd3068_rtc_set_time, +}; + +static const struct regmap_config regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x11, +}; + +static int sd3068_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret; + struct sd3068 *sd3068; + unsigned char rtc_data[NUM_TIME_REGS] = {0}; + pr_debug("probed\n"); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENODEV; + + sd3068 = devm_kzalloc(&client->dev, sizeof(*sd3068), GFP_KERNEL); + if (!sd3068) + return -ENOMEM; + + sd3068->regmap = devm_regmap_init_i2c(client, ®map_config); + if (IS_ERR(sd3068->regmap)) { + dev_err(&client->dev, "regmap allocation failed\n"); + return PTR_ERR(sd3068->regmap); + } + + i2c_set_clientdata(client, sd3068); + + sd3068->rtc = devm_rtc_allocate_device(&client->dev); + if (IS_ERR(sd3068->rtc)) + return PTR_ERR(sd3068->rtc); + + sd3068->rtc->ops = &sd3068_rtc_ops; + sd3068->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + sd3068->rtc->range_max = RTC_TIMESTAMP_END_2099; + + ret = regmap_bulk_read(sd3068->regmap, SD3068_REG_SC, rtc_data, + NUM_TIME_REGS); + if (ret < 0) { + dev_info(&client->dev, "can not read time data when probe\n"); + return ret; + } + + ret = rtc_register_device(sd3068->rtc); + if (ret) + return ret; + + sd3068_enable_reg_write(sd3068); + + return 0; +} + + +static const struct acpi_device_id ds1307_acpi_ids[] = { + { .id = "DS1339", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, ds1307_acpi_ids); + +static const struct i2c_device_id sd3068_id[] = { + { "sd3068", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c,sd3068_id); + +static const struct of_device_id sd3068_of_match[] = { + { .compatible = "wave,sd3068" }, + { } +}; + +static struct i2c_driver sd3068_driver = { + .driver = { + .name = "rtc-sd3068", + .of_match_table = of_match_ptr(sd3068_of_match), + .acpi_match_table = ACPI_PTR(ds1307_acpi_ids), + }, + .probe = sd3068_probe, + .id_table = sd3068_id, +}; + +module_i2c_driver(sd3068_driver); +MODULE_DEVICE_TABLE(of, sd3068_of_match); + +MODULE_DESCRIPTION("RTC driver for SD3068 and similar chips"); +MODULE_LICENSE("GPL"); diff --git a/target/linux/phytium/files-5.10/drivers/spi/spi-phytium-dma.c b/target/linux/phytium/files-5.10/drivers/spi/spi-phytium-dma.c new file mode 100644 index 000000000..4f78d0659 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/spi/spi-phytium-dma.c @@ -0,0 +1,555 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Special handling for phytium DMA core + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "spi-phytium.h" + +#define RX_BUSY 0 +#define RX_BURST_LEVEL 16 +#define TX_BUSY 1 +#define TX_BURST_LEVEL 16 + +#define DMA_MAX_BUF_SIZE 4096 + +static void phytium_spi_dma_maxburst_init(struct phytium_spi *fts) +{ + struct dma_slave_caps caps; + u32 max_burst, def_burst; + int ret; + + def_burst = fts->fifo_len / 2; + + ret = dma_get_slave_caps(fts->rxchan, &caps); + if (!ret && caps.max_burst) + max_burst = caps.max_burst; + else + max_burst = RX_BURST_LEVEL; + + fts->rxburst = min(max_burst, def_burst); + phytium_writel(fts, DMARDLR, 0x0); + + ret = dma_get_slave_caps(fts->txchan, &caps); + if (!ret && caps.max_burst) + max_burst = caps.max_burst; + else + max_burst = TX_BURST_LEVEL; + + /* + * Having a Rx DMA channel serviced with higher priority than a Tx DMA + * channel might not be enough to provide a well balanced DMA-based + * SPI transfer interface. There might still be moments when the Tx DMA + * channel is occasionally handled faster than the Rx DMA channel. + * That in its turn will eventually cause the SPI Rx FIFO overflow if + * SPI bus speed is high enough to fill the SPI Rx FIFO in before it's + * cleared by the Rx DMA channel. In order to fix the problem the Tx + * DMA activity is intentionally slowed down by limiting the SPI Tx + * FIFO depth with a value twice bigger than the Tx burst length. + */ + fts->txburst = min(max_burst, def_burst); + /* set dmatdlr to 0 + 1 */ + phytium_writel(fts, DMATDLR, 0); +} + +static void phytium_spi_dma_sg_burst_init(struct phytium_spi *fts) +{ + struct dma_slave_caps tx = {0}, rx = {0}; + + dma_get_slave_caps(fts->txchan, &tx); + dma_get_slave_caps(fts->rxchan, &rx); + + if (tx.max_sg_burst > 0 && rx.max_sg_burst > 0) + fts->dma_sg_burst = min(tx.max_sg_burst, rx.max_sg_burst); + else if (tx.max_sg_burst > 0) + fts->dma_sg_burst = tx.max_sg_burst; + else if (rx.max_sg_burst > 0) + fts->dma_sg_burst = rx.max_sg_burst; + else + fts->dma_sg_burst = 0; +} + +static int phytium_spi_dma_init(struct device *dev, struct phytium_spi *fts) +{ + fts->rxchan = dma_request_chan(dev, "rx"); + if (IS_ERR_OR_NULL(fts->rxchan)) + return -ENODEV; + + fts->txchan = dma_request_chan(dev, "tx"); + if (IS_ERR_OR_NULL(fts->txchan)) { + dev_err(dev, "can't request chan\n"); + dma_release_channel(fts->rxchan); + fts->rxchan = NULL; + return -ENODEV; + } + + fts->master->dma_rx = fts->rxchan; + fts->master->dma_tx = fts->txchan; + init_completion(&fts->dma_completion); + + phytium_spi_dma_maxburst_init(fts); + phytium_spi_dma_sg_burst_init(fts); + + return 0; +} + +static void phytium_spi_dma_exit(struct phytium_spi *fts) +{ + if (fts->txchan) { + dmaengine_terminate_sync(fts->txchan); + dma_release_channel(fts->txchan); + } + + if (fts->rxchan) { + dmaengine_terminate_sync(fts->rxchan); + dma_release_channel(fts->rxchan); + } +} + +static irqreturn_t phytium_spi_dma_transfer_handler(struct phytium_spi *fts) +{ + phytium_spi_check_status(fts, false); + + complete(&fts->dma_completion); + + return IRQ_HANDLED; +} + +static bool phytium_spi_can_dma(struct spi_controller *master, + struct spi_device *spi, struct spi_transfer *xfer) +{ + struct phytium_spi *fts = spi_controller_get_devdata(master); + + return xfer->len > fts->fifo_len; +} + +static enum dma_slave_buswidth phytium_spi_dma_convert_width(u8 n_bytes) +{ + if (n_bytes == 1) + return DMA_SLAVE_BUSWIDTH_1_BYTE; + else if (n_bytes == 2) + return DMA_SLAVE_BUSWIDTH_2_BYTES; + + return DMA_SLAVE_BUSWIDTH_UNDEFINED; +} + +static int phytium_spi_dma_wait(struct phytium_spi *fts, unsigned int len, + u32 speed) +{ + unsigned long long ms; + + ms = len * MSEC_PER_SEC * BITS_PER_BYTE; + do_div(ms, speed); + ms += ms + 200; + + if (ms > UINT_MAX) + ms = UINT_MAX; + + ms = wait_for_completion_timeout(&fts->dma_completion, + msecs_to_jiffies(ms)); + + if (ms == 0) { + dev_err(&fts->master->cur_msg->spi->dev, + "DMA transaction timed out\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static inline bool phytium_spi_dma_tx_busy(struct phytium_spi *fts) +{ + return !(phytium_readl(fts, SR) & SR_TF_EMPT); +} + +static int phytium_spi_dma_wait_tx_done(struct phytium_spi *fts, + struct spi_transfer *xfer) +{ + int retry = SPI_WAIT_RETRIES; + struct spi_delay delay; + u32 nents; + + nents = phytium_readl(fts, TXFLR); + delay.unit = SPI_DELAY_UNIT_SCK; + delay.value = nents * fts->n_bytes * BITS_PER_BYTE; + + while (phytium_spi_dma_tx_busy(fts) && retry--) + spi_delay_exec(&delay, xfer); + + if (retry < 0) { + dev_err(&fts->master->dev, "Tx hanged up\n"); + return -EIO; + } + + return 0; +} + +/* + * fts->dma_chan_busy is set before the dma transfer starts, callback for tx + * channel will clear a corresponding bit. + */ +static void phytium_spi_dma_tx_done(void *arg) +{ + struct phytium_spi *fts = arg; + + clear_bit(TX_BUSY, &fts->dma_chan_busy); + if (test_bit(RX_BUSY, &fts->dma_chan_busy)) + return; + + complete(&fts->dma_completion); +} + +static int phytium_spi_dma_config_tx(struct phytium_spi *fts) +{ + struct dma_slave_config txconf; + + memset(&txconf, 0, sizeof(txconf)); + txconf.direction = DMA_MEM_TO_DEV; + txconf.dst_addr = fts->dma_addr; + txconf.dst_maxburst = fts->txburst; + txconf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + txconf.dst_addr_width = phytium_spi_dma_convert_width(fts->n_bytes); + txconf.device_fc = false; + + return dmaengine_slave_config(fts->txchan, &txconf); +} + +static int phytium_spi_dma_submit_tx(struct phytium_spi *fts, struct scatterlist *sgl, + unsigned int nents) +{ + struct dma_async_tx_descriptor *txdesc; + dma_cookie_t cookie; + int ret; + + txdesc = dmaengine_prep_slave_sg(fts->txchan, sgl, nents, + DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!txdesc) + return -ENOMEM; + + txdesc->callback = phytium_spi_dma_tx_done; + txdesc->callback_param = fts; + + cookie = dmaengine_submit(txdesc); + ret = dma_submit_error(cookie); + if (ret) { + dmaengine_terminate_sync(fts->txchan); + return ret; + } + + set_bit(TX_BUSY, &fts->dma_chan_busy); + + return 0; +} + +static inline bool phytium_spi_dma_rx_busy(struct phytium_spi *fts) +{ + return !!(phytium_readl(fts, SR) & SR_RF_NOT_EMPT); +} + +static int phytium_spi_dma_wait_rx_done(struct phytium_spi *fts) +{ + int retry = SPI_WAIT_RETRIES; + struct spi_delay delay; + unsigned long ns, us; + u32 nents; + + /* + * It's unlikely that DMA engine is still doing the data fetching, but + * if it's let's give it some reasonable time. The timeout calculation + * is based on the synchronous APB/SSI reference clock rate, on a + * number of data entries left in the Rx FIFO, times a number of clock + * periods normally needed for a single APB read/write transaction + * without PREADY signal utilized (which is true for the phytium APB SSI + * controller). + */ + nents = phytium_readl(fts, RXFLR); + ns = 4U * NSEC_PER_SEC / fts->max_freq * nents; + if (ns <= NSEC_PER_USEC) { + delay.unit = SPI_DELAY_UNIT_NSECS; + delay.value = ns; + } else { + us = DIV_ROUND_UP(ns, NSEC_PER_USEC); + delay.unit = SPI_DELAY_UNIT_USECS; + delay.value = clamp_val(us, 0, USHRT_MAX); + } + + while (phytium_spi_dma_rx_busy(fts) && retry--) + spi_delay_exec(&delay, NULL); + + if (retry < 0) { + dev_err(&fts->master->dev, "Rx hanged up, nents = %d\n", nents); + return -EIO; + } + + return 0; +} + +/* + * fts->dma_chan_busy is set before the dma transfer starts, callback for rx + * channel will clear a corresponding bit. + */ +static void phytium_spi_dma_rx_done(void *arg) +{ + struct phytium_spi *fts = arg; + + clear_bit(RX_BUSY, &fts->dma_chan_busy); + if (test_bit(TX_BUSY, &fts->dma_chan_busy)) + return; + + complete(&fts->dma_completion); +} + +static int phytium_spi_dma_config_rx(struct phytium_spi *fts) +{ + struct dma_slave_config rxconf; + + memset(&rxconf, 0, sizeof(rxconf)); + rxconf.direction = DMA_DEV_TO_MEM; + rxconf.src_addr = fts->dma_addr; + rxconf.src_maxburst = fts->rxburst; + rxconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + rxconf.src_addr_width = phytium_spi_dma_convert_width(fts->n_bytes); + rxconf.device_fc = false; + + return dmaengine_slave_config(fts->rxchan, &rxconf); +} + +static int phytium_spi_dma_submit_rx(struct phytium_spi *fts, struct scatterlist *sgl, + unsigned int nents) +{ + struct dma_async_tx_descriptor *rxdesc; + dma_cookie_t cookie; + int ret; + + rxdesc = dmaengine_prep_slave_sg(fts->rxchan, sgl, nents, + DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!rxdesc) + return -ENOMEM; + + rxdesc->callback = phytium_spi_dma_rx_done; + rxdesc->callback_param = fts; + + cookie = dmaengine_submit(rxdesc); + ret = dma_submit_error(cookie); + if (ret) { + dmaengine_terminate_sync(fts->rxchan); + return ret; + } + + set_bit(RX_BUSY, &fts->dma_chan_busy); + + return 0; +} + +static int phytium_spi_dma_setup(struct phytium_spi *fts, struct spi_transfer *xfer) +{ + u16 imr, dma_ctrl; + int ret; + + if (!xfer->tx_buf) + return -EINVAL; + + /* Setup DMA channels */ + ret = phytium_spi_dma_config_tx(fts); + if (ret) + return ret; + + if (xfer->rx_buf) { + ret = phytium_spi_dma_config_rx(fts); + if (ret) + return ret; + } + + /* Set the DMA handshaking interface */ + dma_ctrl = SPI_DMA_TDMAE; + if (xfer->rx_buf) + dma_ctrl |= SPI_DMA_RDMAE; + phytium_writel(fts, DMACR, dma_ctrl); + + /* Set the interrupt mask */ + imr = INT_TXOI; + if (xfer->rx_buf) + imr |= INT_RXUI | INT_RXOI; + + spi_umask_intr(fts, imr); + + reinit_completion(&fts->dma_completion); + + fts->transfer_handler = phytium_spi_dma_transfer_handler; + + return 0; +} + +static int phytium_spi_dma_transfer_all(struct phytium_spi *fts, + struct spi_transfer *xfer) +{ + int ret; + + /* Submit the DMA Tx transfer */ + ret = phytium_spi_dma_submit_tx(fts, xfer->tx_sg.sgl, xfer->tx_sg.nents); + if (ret) + goto err_clear_dmac; + + /* Submit the DMA Rx transfer if required */ + if (xfer->rx_buf) { + ret = phytium_spi_dma_submit_rx(fts, xfer->rx_sg.sgl, + xfer->rx_sg.nents); + if (ret) + goto err_clear_dmac; + + /* rx must be started before tx due to spi instinct */ + dma_async_issue_pending(fts->rxchan); + } + + dma_async_issue_pending(fts->txchan); + + ret = phytium_spi_dma_wait(fts, xfer->len, xfer->effective_speed_hz); + +err_clear_dmac: + phytium_writel(fts, DMACR, 0); + + return ret; +} + +static int phytium_spi_dma_transfer_one(struct phytium_spi *fts, + struct spi_transfer *xfer) +{ + struct scatterlist *tx_sg = NULL, *rx_sg = NULL, tx_tmp, rx_tmp; + unsigned int tx_len = 0, rx_len = 0; + unsigned int base, len; + int ret; + + sg_init_table(&tx_tmp, 1); + sg_init_table(&rx_tmp, 1); + + for (base = 0, len = 0; base < xfer->len; base += len) { + /* Fetch next Tx DMA data chunk */ + if (!tx_len) { + tx_sg = !tx_sg ? &xfer->tx_sg.sgl[0] : sg_next(tx_sg); + sg_dma_address(&tx_tmp) = sg_dma_address(tx_sg); + tx_len = sg_dma_len(tx_sg); + } + + /* Fetch next Rx DMA data chunk */ + if (!rx_len) { + rx_sg = !rx_sg ? &xfer->rx_sg.sgl[0] : sg_next(rx_sg); + sg_dma_address(&rx_tmp) = sg_dma_address(rx_sg); + rx_len = sg_dma_len(rx_sg); + } + + if ((base + DMA_MAX_BUF_SIZE) > xfer->len) + len = xfer->len - base; + else + len = DMA_MAX_BUF_SIZE; + + len = min3(len, tx_len, rx_len); + + sg_dma_len(&tx_tmp) = len; + sg_dma_len(&rx_tmp) = len; + + /* Submit DMA Tx transfer */ + ret = phytium_spi_dma_submit_tx(fts, &tx_tmp, 1); + if (ret) + break; + + /* Submit DMA Rx transfer */ + ret = phytium_spi_dma_submit_rx(fts, &rx_tmp, 1); + if (ret) + break; + + /* Rx must be started before Tx due to SPI instinct */ + dma_async_issue_pending(fts->rxchan); + + dma_async_issue_pending(fts->txchan); + + /* + * Here we only need to wait for the DMA transfer to be + * finished since SPI controller is kept enabled during the + * procedure this loop implements and there is no risk to lose + * data left in the Tx/Rx FIFOs. + */ + ret = phytium_spi_dma_wait(fts, len, xfer->effective_speed_hz); + if (ret) + break; + + reinit_completion(&fts->dma_completion); + + sg_dma_address(&tx_tmp) += len; + sg_dma_address(&rx_tmp) += len; + tx_len -= len; + rx_len -= len; + } + + phytium_writel(fts, DMACR, 0); + + return ret; +} + +static int phytium_spi_dma_transfer(struct phytium_spi *fts, struct spi_transfer *xfer) +{ + unsigned int nents; + int ret; + + nents = max(xfer->tx_sg.nents, xfer->rx_sg.nents); + + /* + * large transfer length caused spi RX FIFO full event + * transfer 4096 bytes each time + */ + if (xfer->len <= DMA_MAX_BUF_SIZE) + ret = phytium_spi_dma_transfer_all(fts, xfer); + else + ret = phytium_spi_dma_transfer_one(fts, xfer); + if (ret) + return ret; + + if (fts->master->cur_msg->status == -EINPROGRESS) { + ret = phytium_spi_dma_wait_tx_done(fts, xfer); + if (ret) + return ret; + } + + if (xfer->rx_buf && fts->master->cur_msg->status == -EINPROGRESS) + ret = phytium_spi_dma_wait_rx_done(fts); + + return ret; +} + +static void phytium_spi_dma_stop(struct phytium_spi *fts) +{ + if (test_bit(TX_BUSY, &fts->dma_chan_busy)) { + dmaengine_terminate_sync(fts->txchan); + clear_bit(TX_BUSY, &fts->dma_chan_busy); + } + if (test_bit(RX_BUSY, &fts->dma_chan_busy)) { + dmaengine_terminate_sync(fts->rxchan); + clear_bit(RX_BUSY, &fts->dma_chan_busy); + } +} + +static const struct phytium_spi_dma_ops phytium_spi_dma_generic_ops = { + .dma_init = phytium_spi_dma_init, + .dma_exit = phytium_spi_dma_exit, + .dma_setup = phytium_spi_dma_setup, + .can_dma = phytium_spi_can_dma, + .dma_transfer = phytium_spi_dma_transfer, + .dma_stop = phytium_spi_dma_stop, +}; + +void phytium_spi_dmaops_set(struct phytium_spi *fts) +{ + fts->dma_ops = &phytium_spi_dma_generic_ops; +} +EXPORT_SYMBOL_GPL(phytium_spi_dmaops_set); + +MODULE_LICENSE("GPL v2"); diff --git a/target/linux/phytium/files-5.10/drivers/spi/spi-phytium-pci.c b/target/linux/phytium/files-5.10/drivers/spi/spi-phytium-pci.c new file mode 100644 index 000000000..4b83bc1b5 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/spi/spi-phytium-pci.c @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Phytium SPI core controller PCI driver. + * + * Copyright (c) 2019-2023 Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "spi-phytium.h" + +#define DRIVER_NAME "phytium_spi_pci" + +static int phytium_spi_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct phytium_spi *fts; + int pci_bar = 0; + int ret; + + fts = devm_kzalloc(&pdev->dev, sizeof(struct phytium_spi), + GFP_KERNEL); + if (!fts) + return -ENOMEM; + + ret = pcim_enable_device(pdev); + if (ret) + return ret; + + ret = pcim_iomap_regions(pdev, 1 << pci_bar, pci_name(pdev)); + if (ret) { + dev_err(&pdev->dev, "pci iomap failed?\n"); + return ret; + } + + fts->regs = pcim_iomap_table(pdev)[pci_bar]; + if (IS_ERR(fts->regs)) { + dev_err(&pdev->dev, "SPI region map failed\n"); + return PTR_ERR(fts->regs); + } + + fts->irq = pdev->irq; + if (fts->irq < 0) { + dev_err(&pdev->dev, "no irq resource?\n"); + return fts->irq; /* -ENXIO */ + } + + fts->bus_num = -1; + + fts->max_freq = 48000000; + + fts->num_cs = 4; + + fts->global_cs = 1; + + ret = phytium_spi_add_host(&pdev->dev, fts); + if (ret) + return ret; + + pci_set_drvdata(pdev, fts); + return 0; +} + +static void phytium_spi_pci_remove(struct pci_dev *pdev) +{ + struct phytium_spi *fts = pci_get_drvdata(pdev); + + phytium_spi_remove_host(fts); +} + + +#ifdef CONFIG_PM_SLEEP +static int spi_suspend(struct device *dev) +{ + struct phytium_spi *fts = dev_get_drvdata(dev); + + return phytium_spi_suspend_host(fts); +} + +static int spi_resume(struct device *dev) +{ + struct phytium_spi *fts = dev_get_drvdata(dev); + + return phytium_spi_resume_host(fts); +} +#endif + +static SIMPLE_DEV_PM_OPS(phytium_spi_pm_ops, spi_suspend, spi_resume); + +static const struct pci_device_id phytium_device_pci_tbl[] = { + { PCI_VDEVICE(PHYTIUM, 0xdc2c) }, + {}, +}; + +static struct pci_driver phytium_spi_pci_driver = { + .name = DRIVER_NAME, + .id_table = phytium_device_pci_tbl, + .probe = phytium_spi_pci_probe, + .remove = phytium_spi_pci_remove, + .driver = { + .pm = &phytium_spi_pm_ops, + } +}; + +module_pci_driver(phytium_spi_pci_driver); + +MODULE_AUTHOR("Yiqun Zhang "); +MODULE_DESCRIPTION("PCI Driver for Phytium SPI controller core"); +MODULE_LICENSE("GPL v2"); diff --git a/target/linux/phytium/files-5.10/drivers/spi/spi-phytium-plat.c b/target/linux/phytium/files-5.10/drivers/spi/spi-phytium-plat.c new file mode 100644 index 000000000..51805f1b0 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/spi/spi-phytium-plat.c @@ -0,0 +1,204 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Phytium SPI core controller platform driver. + * + * Copyright (c) 2019-2023 Phytium Technology Co., Ltd. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "spi-phytium.h" + +#define DRIVER_NAME "phytium_spi" + +static int phytium_spi_probe(struct platform_device *pdev) +{ + struct phytium_spi *fts; + struct resource *mem; + int ret; + int num_cs; + int cs_gpio; + int global_cs; + int i; + + fts = devm_kzalloc(&pdev->dev, sizeof(struct phytium_spi), + GFP_KERNEL); + if (!fts) + return -ENOMEM; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(&pdev->dev, "no mem resource?\n"); + return -EINVAL; + } + + fts->paddr = mem->start; + fts->regs = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(fts->regs)) { + dev_err(&pdev->dev, "SPI region map failed\n"); + return PTR_ERR(fts->regs); + } + + fts->irq = platform_get_irq(pdev, 0); + if (fts->irq < 0) { + dev_err(&pdev->dev, "no irq resource?\n"); + return fts->irq; /* -ENXIO */ + } + + if (pdev->dev.of_node) { + fts->clk = devm_clk_get(&pdev->dev, NULL); + + if (IS_ERR(fts->clk)) + return PTR_ERR(fts->clk); + ret = clk_prepare_enable(fts->clk); + if (ret) + return ret; + + fts->max_freq = clk_get_rate(fts->clk); + } else if (has_acpi_companion(&pdev->dev)) { + fts->max_freq = 48000000; + } + + fts->bus_num = pdev->id; + device_property_read_u32(&pdev->dev, "reg-io-width", &fts->reg_io_width); + + num_cs = 4; + device_property_read_u32(&pdev->dev, "num-cs", &num_cs); + fts->num_cs = num_cs; + + if (pdev->dev.of_node) { + int i; + + for (i = 0; i < fts->num_cs; i++) { + cs_gpio = of_get_named_gpio(pdev->dev.of_node, + "cs-gpios", i); + + if (cs_gpio == -EPROBE_DEFER) { + ret = cs_gpio; + goto out; + } + + if (gpio_is_valid(cs_gpio)) { + ret = devm_gpio_request(&pdev->dev, cs_gpio, + dev_name(&pdev->dev)); + if (ret) + goto out; + } + } + } else if(has_acpi_companion(&pdev->dev)) { + int n; + int *cs; + struct gpio_desc *gpiod; + + n = gpiod_count(&pdev->dev, "cs"); + + cs = devm_kcalloc(&pdev->dev, n, sizeof(int), GFP_KERNEL); + fts->cs = cs; + + for (i = 0; i < n; i++) { + gpiod = devm_gpiod_get_index_optional(&pdev->dev, "cs", i, + GPIOD_OUT_LOW); + + if (IS_ERR(gpiod)) { + ret = PTR_ERR(gpiod); + goto out; + } + + cs_gpio = desc_to_gpio(gpiod); + cs[i] = cs_gpio; + } + } + + device_property_read_u32(&pdev->dev, "global-cs", &global_cs); + fts->global_cs = global_cs; + + /* check is use dma transfer */ + if ((device_property_read_string_array(&pdev->dev, "dma-names", + NULL, 0) > 0) && + device_property_present(&pdev->dev, "dmas")) { + fts->dma_en = true; + phytium_spi_dmaops_set(fts); + } + + ret = phytium_spi_add_host(&pdev->dev, fts); + if (ret) + goto out; + + platform_set_drvdata(pdev, fts); + return 0; + +out: + clk_disable_unprepare(fts->clk); + return ret; +} + +static int phytium_spi_remove(struct platform_device *pdev) +{ + struct phytium_spi *fts = platform_get_drvdata(pdev); + + phytium_spi_remove_host(fts); + clk_disable_unprepare(fts->clk); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int spi_suspend(struct device *dev) +{ + struct phytium_spi *fts = dev_get_drvdata(dev); + + return phytium_spi_suspend_host(fts); +} + +static int spi_resume(struct device *dev) +{ + struct phytium_spi *fts = dev_get_drvdata(dev); + + return phytium_spi_resume_host(fts); +} +#endif + +static SIMPLE_DEV_PM_OPS(phytium_spi_pm_ops, spi_suspend, spi_resume); + +static const struct of_device_id phytium_spi_of_match[] = { + { .compatible = "phytium,spi", .data = (void *)0 }, + { /* end of table */} +}; +MODULE_DEVICE_TABLE(of, phytium_spi_of_match); + +static const struct acpi_device_id phytium_spi_acpi_match[] = { + {"PHYT000E", 0}, + {} +}; +MODULE_DEVICE_TABLE(acpi, phytium_spi_acpi_match); + +static struct platform_driver phytium_spi_driver = { + .probe = phytium_spi_probe, + .remove = phytium_spi_remove, + .driver = { + .name = DRIVER_NAME, + .of_match_table = of_match_ptr(phytium_spi_of_match), + .acpi_match_table = ACPI_PTR(phytium_spi_acpi_match), + .pm = &phytium_spi_pm_ops, + }, +}; +module_platform_driver(phytium_spi_driver); + +MODULE_AUTHOR("Yiqun Zhang "); +MODULE_DESCRIPTION("Platform Driver for Phytium SPI controller core"); +MODULE_LICENSE("GPL v2"); diff --git a/target/linux/phytium/files-5.10/drivers/spi/spi-phytium-qspi.c b/target/linux/phytium/files-5.10/drivers/spi/spi-phytium-qspi.c new file mode 100755 index 000000000..e909a8505 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/spi/spi-phytium-qspi.c @@ -0,0 +1,801 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Phytium Quad SPI controller driver. + * + * Copyright (c) 2022-2023, Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +#define QSPI_FLASH_CAP_REG 0x00 +#define QSPI_FLASH_CAP_NUM_SHIFT 3 +#define QSPI_FLASH_CAP_NUM_MASK (0x3 << QSPI_FLASH_CAP_NUM_SHIFT) +#define QSPI_FLASH_CAP_CAP_SHIFT 0 +#define QSPI_FLASH_CAP_CAP_MASK (0x7 << QSPI_FLASH_CAP_CAP_SHIFT) + +#define QSPI_RD_CFG_REG 0x04 +#define QSPI_RD_CFG_RD_CMD_SHIFT 24 +#define QSPI_RD_CFG_RD_CMD_MASK (0xff << QSPI_RD_CFG_RD_CMD_SHIFT) +#define QSPI_RD_CFG_RD_THROUGH_SHIFT 23 +#define QSPI_RD_CFG_RD_THROUGH_MASK (0x1 << QSPI_RD_CFG_RD_THROUGH_SHIFT) +#define QSPI_RD_CFG_RD_TRANSFER_SHIFT 20 +#define QSPI_RD_CFG_RD_TRANSFER_MASK (0x7 << QSPI_RD_CFG_RD_TRANSFER_SHIFT) +#define QSPI_RD_CFG_RD_ADDR_SEL_SHIFT 19 +#define QSPI_RD_CFG_RD_ADDR_SEL_MASK (0x1 << QSPI_RD_CFG_RD_ADDR_SEL_SHIFT) +#define QSPI_RD_CFG_RD_LATENCY_SHIFT 18 +#define QSPI_RD_CFG_RD_LATENCY_MASK (0x1 << QSPI_RD_CFG_RD_LATENCY_SHIFT) +#define QSPI_RD_CFG_MODE_BYTE_SHIFT 17 +#define QSPI_RD_CFG_MODE_BYTE_MASK (0x1 << QSPI_RD_CFG_MODE_BYTE_SHIFT) +#define QSPI_RD_CFG_CMD_SIGN_SHIFT 9 +#define QSPI_RD_CFG_CMD_SIGN_MASK (0xff << QSPI_RD_CFG_CMD_SIGN_SHIFT) +#define QSPI_RD_CFG_DUMMY_SHIFT 4 +#define QSPI_RD_CFG_DUMMY_MASK (0x1f << QSPI_RD_CFG_DUMMY_SHIFT) +#define QSPI_RD_CFG_D_BUFFER_SHIFT 3 +#define QSPI_RD_CFG_D_BUFFER_MASK (0x1 << QSPI_RD_CFG_D_BUFFER_SHIFT) +#define QSPI_RD_CFG_RD_SCK_SEL_SHIFT 0 +#define QSPI_RD_CFG_RD_SCK_SEL_MASK (0x7 << QSPI_RD_CFG_RD_SCK_SEL_SHIFT) + +#define QSPI_WR_CFG_REG 0x08 +#define QSPI_WR_CFG_WR_CMD_SHIFT 24 +#define QSPI_WR_CFG_WR_CMD_MASK (0xff << QSPI_WR_CFG_WR_CMD_SHIFT) +#define QSPI_WR_CFG_WR_WAIT_SHIFT 9 +#define QSPI_WR_CFG_WR_WAIT_MASK (0x01 << QSPI_WR_CFG_WR_WAIT_SHIFT) +#define QSPI_WR_CFG_WR_THROUGH_SHIFT 8 +#define QSPI_WR_CFG_WR_THROUGH_MASK (0x01 << QSPI_WR_CFG_WR_THROUGH_SHIFT) +#define QSPI_WR_CFG_WR_TRANSFER_SHIFT 5 +#define QSPI_WR_CFG_WR_TRANSFER_MASK (0X7 << QSPI_WR_CFG_WR_TRANSFER_SHIFT) +#define QSPI_WR_CFG_WR_ADDR_SEL_SHIFT 4 +#define QSPI_WR_CFG_WR_ADDR_SEL_MASK (0x1 << QSPI_WR_CFG_WR_ADDR_SEL_SHIFT) +#define QSPI_WR_CFG_WR_MODE_SHIFT 3 +#define QSPI_WR_CFG_WR_MODE_MASK (0x1 << QSPI_WR_CFG_WR_MODE_SHIFT) +#define QSPI_WR_CFG_WR_SCK_SEL_SHIFT 0 +#define QSPI_WR_CFG_WR_SCK_SEL_MASK (0x7 << QSPI_WR_CFG_WR_SCK_SEL_SHIFT) + +#define QSPI_FLUSH_REG 0x0c +#define QSPI_FLUSH_EN (0x1 << 0) + +#define QSPI_CMD_PORT_REG 0x10 +#define QSPI_CMD_PORT_CMD_SHIFT 24 +#define QSPI_CMD_PORT_CMD_MASK (0xff << QSPI_CMD_PORT_CMD_SHIFT) +#define QSPI_CMD_PORT_WAIT_SHIFT 22 +#define QSPI_CMD_PORT_WAIT_MASK (0x1 << QSPI_CMD_PORT_WAIT_SHIFT) +#define QSPI_CMD_PORT_THROUGH_SHIFT 21 +#define QSPI_CMD_PORT_THROUGH_MASK (0x1 << QSPI_CMD_PORT_THROUGH_SHIFT) +#define QSPI_CMD_PORT_CS_SHIFT 19 +#define QSPI_CMD_PORT_CS_MASK (0x3 << QSPI_CMD_PORT_CS_SHIFT) +#define QSPI_CMD_PORT_TRANSFER_SHIFT 16 +#define QSPI_CMD_PORT_TRANSFER_MASK (0x7 << QSPI_CMD_PORT_TRANSFER_SHIFT) +#define QSPI_CMD_PORT_CMD_ADDR_SHIFT 15 +#define QSPI_CMD_PORT_CMD_ADDR_MASK (0x1 << QSPI_CMD_PORT_CMD_ADDR_SHIFT) +#define QSPI_CMD_PORT_LATENCY_SHIFT 14 +#define QSPI_CMD_PORT_LATENCY_MASK (0x1 << QSPI_CMD_PORT_LATENCY_SHIFT) +#define QSPI_CMD_PORT_DATA_XFER_SHIFT 13 +#define QSPI_CMD_PORT_DATA_XFER_MASK (0x1 << QSPI_CMD_PORT_DATA_XFER_SHIFT) +#define QSPI_CMD_PORT_ADDR_SEL_SHIFT 12 +#define QSPI_CMD_PORT_ADDR_SEL_MASK (0x1 << QSPI_CMD_PORT_ADDR_SEL_SHIFT) +#define QSPI_CMD_PORT_DUMMY_SHIFT 7 +#define QSPI_CMD_PORT_DUMMY_MASK (0x1f << QSPI_CMD_PORT_DUMMY_SHIFT) +#define QSPI_CMD_PORT_P_BUFFER_SHIFT 6 +#define QSPI_CMD_PORT_P_BUFFER_MASK (0x1 << QSPI_CMD_PORT_P_BUFFER_SHIFT) +#define QSPI_CMD_PORT_RW_NUM_SHIFT 3 +#define QSPI_CMD_PORT_RW_NUM_MASK (0x7 << QSPI_CMD_PORT_RW_NUM_SHIFT) +#define QSPI_CMD_PORT_SCK_SEL_SHIFT 0 +#define QSPI_CMD_PORT_SCK_SEL_MASK (0x7 << QSPI_CMD_PORT_SCK_SEL_SHIFT) + +#define QSPI_ADDR_PORT_REG 0x14 +#define QSPI_HD_PORT_REG 0x18 +#define QSPI_LD_PORT_REG 0x1c + +#define QSPI_FUN_SET_REG 0x20 +#define QSPI_FUN_SET_HOLD_SHIFT 24 +#define QSPI_FUN_SET_HOLD_MASK (0xff << QSPI_FUN_SET_HOLD_SHIFT) +#define QSPI_FUN_SET_SETUP_SHIFT 16 +#define QSPI_FUN_SET_SETUP_MASK (0xff << QSPI_FUN_SET_SETUP_SHIFT) +#define QSPI_FUN_SET_DELAY_SHIFT 0 +#define QSPI_FUN_SET_DELAY_MASK (0xffff << QSPI_FUN_SET_DELAY_SHIFT) + +#define QSPI_WIP_REG 0x24 +#define QSPI_WIP_W_CMD_SHIFT 24 +#define QSPI_WIP_W_CMD_MASK (0xff << QSPI_WIP_W_CMD_SHIFT) +#define QSPI_WIP_W_TRANSFER_SHIFT 3 +#define QSPI_WIP_W_TRANSFER_MASK (0x3 << QSPI_WIP_W_TRANSFER_SHIFT) +#define QSPI_WIP_W_SCK_SEL_SHIFT 0 +#define QSPI_WIP_W_SCK_SEL_MASK (0x7 << QSPI_WIP_W_SCK_SEL_SHIFT) + +#define QSPI_WP_REG 0x28 +#define QSPI_WP_EN_SHIFT 17 +#define QSPI_WP_EN_MASK (0x1 << QSPI_WP_EN_SHIFT) +#define QSPI_WP_IO2_SHIFT 16 +#define QSPI_WP_IO2_MASK (0x1 << QSPI_WP_IO2_SHIFT) +#define QSPI_WP_HOLD_SHIFT 8 +#define QSPI_WP_HOLD_MASK (0xff << QSPI_WP_HOLD_SHIFT) +#define QSPI_WP_SETUP_SHIFT 0 +#define QSPI_WP_SETUP_MASK (0xff << QSPI_WP_SETUP_SHIFT) + +#define QSPI_MODE_REG 0x2c +#define QSPI_MODE_VALID_SHIFT 8 +#define QSPI_MODE_VALID_MASK (0xff << QSPI_MODE_VALID_SHIFT) +#define QSPI_MODE_SHIFT 0 +#define QSPI_MODE_MASK (0xff << QSPI_MODE_SHIFT) + +#define PHYTIUM_QSPI_MAX_NORCHIP 4 +#define PHYTIUM_QSPI_MAX_MMAP_SZ (SZ_256M * PHYTIUM_QSPI_MAX_NORCHIP) +#define PHYTIUM_QSPI_MAX_XFER_SZ 8 +#define PHYTIUM_QSPI_DEFAULT_SCK_SEL 5 + +#define XFER_PROTO_1_1_1 0x0 +#define XFER_PROTO_1_1_2 0x1 +#define XFER_PROTO_1_1_4 0x2 +#define XFER_PROTO_1_2_2 0x3 +#define XFER_PROTO_1_4_4 0x4 +#define XFER_PROTO_2_2_2 0x5 +#define XFER_PROTO_4_4_4 0x6 + +struct phytium_qspi_flash { + u32 cs; + u32 clk_div; + + void __iomem *base; + resource_size_t size; + struct spi_device *spi; +}; + +struct phytium_qspi { + struct device *dev; + struct spi_controller *ctrl; + + void __iomem *io_base; + void __iomem *mm_base; + resource_size_t mm_size; + resource_size_t used_size; + + struct clk *clk; + u32 clk_rate; + + struct phytium_qspi_flash flash[PHYTIUM_QSPI_MAX_NORCHIP]; + u8 fnum; + bool nodirmap; +}; + +static bool phytium_qspi_check_buswidth(u8 width) +{ + switch (width) { + case 1: + case 2: + case 4: + return 0; + } + + return -ENOTSUPP; +} + +static uint phytium_spi_nor_clac_clk_div(int div) +{ + uint clk_div = 0; + + if (div <= 2) + clk_div = 1; + else if (div <= 4) + clk_div = 2; + else if (div <= 8) + clk_div = 3; + else if (div <= 16) + clk_div = 4; + else if (div <= 32) + clk_div = 5; + else if (div <= 64) + clk_div = 6; + else if (div <= 128) + clk_div = 7; + else + clk_div = 65535; + + return clk_div; +} + +static int phytium_spi_nor_protocol_encode(const struct spi_mem_op *op, u32 *code) +{ + int ret = 0; + + if (op->cmd.buswidth == 1 && + op->addr.buswidth == 1 && + op->data.buswidth == 1) + *code = XFER_PROTO_1_1_1; + else if (op->cmd.buswidth == 1 && + op->addr.buswidth == 1 && + op->data.buswidth == 2) + *code = XFER_PROTO_1_1_2; + else if (op->cmd.buswidth == 1 && + op->addr.buswidth == 1 && + op->data.buswidth == 4) + *code = XFER_PROTO_1_1_4; + else if (op->cmd.buswidth == 1 && + op->addr.buswidth == 2 && + op->data.buswidth == 2) + *code = XFER_PROTO_1_2_2; + else if (op->cmd.buswidth == 1 && + op->addr.buswidth == 4 && + op->data.buswidth == 4) + *code = XFER_PROTO_1_4_4; + else if (op->cmd.buswidth == 2 && + op->addr.buswidth == 2 && + op->data.buswidth == 2) + *code = XFER_PROTO_2_2_2; + else if (op->cmd.buswidth == 4 && + op->addr.buswidth == 4 && + op->data.buswidth == 4) + *code = XFER_PROTO_4_4_4; + else + *code = XFER_PROTO_1_1_1; + + return ret; +} + +static int phytium_qspi_flash_capacity_encode(u32 size, u32 *cap) +{ + int ret = 0; + + switch (size) { + case SZ_4M: + *cap = 0x0; + break; + case SZ_8M: + *cap = 0x1; + break; + case SZ_16M: + *cap = 0x2; + break; + case SZ_32M: + *cap = 0x3; + break; + case SZ_64M: + *cap = 0x4; + break; + case SZ_128M: + *cap = 0x5; + break; + case SZ_256M: + *cap = 0x6; + break; + case SZ_512M: + *cap = 0x7; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int phytium_qspi_write_port(struct phytium_qspi *qspi, + const u8 *buf, const size_t len) +{ + u32 bouncebuf[2] = { 0 }; + + if (len > PHYTIUM_QSPI_MAX_XFER_SZ) { + dev_err(qspi->dev, "WRITE data exceeds 8 bytes.\n"); + return -EINVAL; + } + + memcpy(bouncebuf, buf, len); + + if (len > 4) + writel_relaxed(bouncebuf[1], qspi->io_base + QSPI_HD_PORT_REG); + writel_relaxed(bouncebuf[0], qspi->io_base + QSPI_LD_PORT_REG); + + return 0; +} + +static int phytium_qspi_read_port(struct phytium_qspi *qspi, + u8 *buf, size_t len) +{ + u32 bouncebuf[2] = { 0 }; + + if (len > PHYTIUM_QSPI_MAX_XFER_SZ) { + dev_err(qspi->dev, "READ data exceeds 8 bytes.\n"); + return -EINVAL; + } + + /* Dummy write to LD_PORT register and issue READ ops*/ + writel_relaxed(0, qspi->io_base + QSPI_LD_PORT_REG); + + /* Read data */ + bouncebuf[0] = readl_relaxed(qspi->io_base + QSPI_LD_PORT_REG); + if (len > 4) + bouncebuf[1] = readl_relaxed(qspi->io_base + QSPI_HD_PORT_REG); + + memcpy(buf, bouncebuf, len); + + return 0; +} + +static int phytium_qspi_adjust_op_size(struct spi_mem *mem, + struct spi_mem_op *op) +{ + if (op->data.nbytes > PHYTIUM_QSPI_MAX_XFER_SZ) + op->data.nbytes = PHYTIUM_QSPI_MAX_XFER_SZ; + + return 0; +} + +static bool phytium_qspi_supports_op(struct spi_mem *mem, + const struct spi_mem_op *op) +{ + int ret; + + ret = phytium_qspi_check_buswidth(op->cmd.buswidth); + + if (op->addr.nbytes) + ret |= phytium_qspi_check_buswidth(op->addr.buswidth); + + if (op->dummy.nbytes) + ret |= phytium_qspi_check_buswidth(op->dummy.buswidth); + + if (op->data.nbytes) + ret |= phytium_qspi_check_buswidth(op->data.buswidth); + + if (ret) + return false; + + /* Max 32 dummy clock cycles supported */ + if (op->dummy.nbytes && + (op->dummy.nbytes * 8 / op->dummy.buswidth > 32)) + return false; + + return spi_mem_default_supports_op(mem, op); +} + +static int phytium_qspi_exec_op(struct spi_mem *mem, + const struct spi_mem_op *op) +{ + struct phytium_qspi *qspi = spi_controller_get_devdata(mem->spi->master); + struct phytium_qspi_flash *flash = &qspi->flash[mem->spi->chip_select]; + u32 cmd, transfer; + int ret; + + dev_dbg(qspi->dev, "cmd:%#x mode: %d.%d.%d.%d addr:%#llx len:%#x\n", + op->cmd.opcode, op->cmd.buswidth, op->addr.buswidth, + op->dummy.buswidth, op->data.buswidth, op->addr.val, + op->data.nbytes); + + cmd = op->cmd.opcode << QSPI_CMD_PORT_CMD_SHIFT; + cmd |= flash->cs << QSPI_CMD_PORT_CS_SHIFT; + + ret = phytium_spi_nor_protocol_encode(op, &transfer); + if (ret) { + dev_err(qspi->dev, "Unsupported SPI NOR protocol.\n"); + goto out; + } + cmd |= transfer << QSPI_CMD_PORT_TRANSFER_SHIFT; + + if (op->addr.nbytes) { + cmd |= QSPI_CMD_PORT_CMD_ADDR_MASK; + if (op->addr.nbytes == 4) + cmd |= QSPI_CMD_PORT_ADDR_SEL_MASK; + + /* Write target address to ADDR_PORT register */ + writel_relaxed(op->addr.val, qspi->io_base + QSPI_ADDR_PORT_REG); + } + + if (op->dummy.nbytes) { + cmd |= QSPI_CMD_PORT_LATENCY_MASK; + cmd |= ((op->dummy.nbytes * 8) / op->dummy.buswidth) << + QSPI_CMD_PORT_LATENCY_SHIFT; + } + + if (op->data.nbytes) { + cmd |= QSPI_CMD_PORT_DATA_XFER_MASK; + cmd &= ~QSPI_CMD_PORT_P_BUFFER_MASK; + cmd |= (op->data.nbytes-1) << QSPI_CMD_PORT_RW_NUM_SHIFT; + } + + cmd |= flash->clk_div; + writel_relaxed(cmd, qspi->io_base + QSPI_CMD_PORT_REG); + + if (op->data.dir == SPI_MEM_DATA_IN) { + ret = phytium_qspi_read_port(qspi, op->data.buf.in, op->data.nbytes); + if (ret) { + dev_err(qspi->dev, "Failed to read data from the port.\n"); + goto out; + } + } else if (op->data.dir == SPI_MEM_DATA_OUT) { + ret = phytium_qspi_write_port(qspi, op->data.buf.out, op->data.nbytes); + if (ret) { + dev_err(qspi->dev, "Failed to write data to the port.\n"); + goto out; + } + } else { + /* Dummy write to LD_PORT register and issue the command */ + writel_relaxed(0, qspi->io_base + QSPI_LD_PORT_REG); + } + +out: + return ret; +} + +static int phytium_qspi_dirmap_create(struct spi_mem_dirmap_desc *desc) +{ + struct spi_device *spi = desc->mem->spi; + struct phytium_qspi *qspi = spi_controller_get_devdata(spi->master); + struct phytium_qspi_flash *flash = &qspi->flash[spi->chip_select]; + struct spi_nor *nor = spi_mem_get_drvdata(desc->mem); + u32 cmd, transfer; + int ret = 0; + + if (!qspi->mm_base || !qspi->mm_size) { + ret = -EOPNOTSUPP; + goto out; + } + + if (!flash->base) { + flash->base = qspi->mm_base + qspi->used_size; + qspi->used_size += nor->mtd.size; + } + + /* Setup RD/WR_CFG register */ + if (desc->info.op_tmpl.data.dir == SPI_MEM_DATA_IN) { + cmd = desc->info.op_tmpl.cmd.opcode << QSPI_RD_CFG_RD_CMD_SHIFT; + ret = phytium_spi_nor_protocol_encode(&desc->info.op_tmpl, &transfer); + if (ret) { + dev_err(qspi->dev, "Unsupported SPI NOR protocol.\n"); + goto out; + } + cmd |= transfer << QSPI_RD_CFG_RD_TRANSFER_SHIFT; + + if (desc->info.op_tmpl.addr.nbytes == 4) + cmd |= QSPI_RD_CFG_RD_ADDR_SEL_MASK; + + if (nor->read_dummy) { + cmd |= QSPI_RD_CFG_RD_LATENCY_MASK; + cmd |= (nor->read_dummy - 1) << QSPI_RD_CFG_DUMMY_SHIFT; + } + + cmd |= QSPI_RD_CFG_D_BUFFER_MASK; + cmd |= flash->clk_div & QSPI_RD_CFG_RD_SCK_SEL_MASK; + + writel_relaxed(cmd, qspi->io_base + QSPI_RD_CFG_REG); + + dev_dbg(qspi->dev, "Create read dirmap and setup RD_CFG_REG [%#x].\n", cmd); + } else if (desc->info.op_tmpl.data.dir == SPI_MEM_DATA_OUT) { + cmd = desc->info.op_tmpl.cmd.opcode << QSPI_WR_CFG_WR_CMD_SHIFT; + ret = phytium_spi_nor_protocol_encode(&desc->info.op_tmpl, &transfer); + if (ret) { + dev_err(qspi->dev, "Unsupported SPI NOR protocol.\n"); + goto out; + } + cmd |= transfer << QSPI_WR_CFG_WR_TRANSFER_SHIFT; + + if (desc->info.op_tmpl.addr.nbytes == 4) + cmd |= QSPI_WR_CFG_WR_ADDR_SEL_MASK; + + cmd |= QSPI_WR_CFG_WR_MODE_MASK; + cmd |= flash->clk_div & QSPI_WR_CFG_WR_SCK_SEL_MASK; + + writel_relaxed(cmd, qspi->io_base + QSPI_WR_CFG_REG); + + dev_dbg(qspi->dev, "Create write dirmap and setup WR_CFG_REG [%#x].\n", cmd); + } else { + ret = -EINVAL; + } + +out: + return ret; +} + +static ssize_t phytium_qspi_dirmap_read(struct spi_mem_dirmap_desc *desc, + u64 offs, size_t len, void *buf) +{ + struct spi_device *spi = desc->mem->spi; + struct phytium_qspi *qspi = spi_controller_get_devdata(spi->master); + struct phytium_qspi_flash *flash = &qspi->flash[spi->chip_select]; + + void __iomem *src = flash->base + offs; + u8 *buf_rx = buf; + + memcpy_fromio(buf_rx, src, len); + + return len; +} + +static ssize_t phytium_qspi_dirmap_write(struct spi_mem_dirmap_desc *desc, + u64 offs, size_t len, const void *buf) +{ + struct spi_device *spi = desc->mem->spi; + struct phytium_qspi *qspi = spi_controller_get_devdata(spi->master); + struct phytium_qspi_flash *flash = &qspi->flash[spi->chip_select]; + + void __iomem *dst = flash->base + offs; + void __iomem *addr; + int i; + size_t mask = 0x03; + u_char tmp[4] = {0}; + + if (offs & 0x03) { + dev_err(qspi->dev, "Addr not four-byte aligned!\n"); + return -EINVAL; + } + + for (i = 0; i < len / 4; i++) + writel_relaxed(*(u32 *)(buf + 4 * i), dst + 4 * i); + + if (len & mask) { + addr = dst + (len & ~mask); + memcpy(tmp, buf + (len & ~mask), len & mask); + writel_relaxed(*(u32 *)(tmp), addr); + } + + //write cache data to flash + writel_relaxed(QSPI_FLUSH_EN, qspi->io_base + QSPI_FLUSH_REG); + + return len; +} + +static int phytium_qspi_setup(struct spi_device *spi) +{ + struct spi_controller *ctrl = spi->master; + struct phytium_qspi *qspi = spi_controller_get_devdata(ctrl); + struct phytium_qspi_flash *flash; + uint clk_div; + + if (ctrl->busy) + return -EBUSY; + + flash = &qspi->flash[spi->chip_select]; + + flash->cs = spi->chip_select; + flash->spi = spi; + if (flash->cs >= PHYTIUM_QSPI_MAX_NORCHIP) { + dev_err(qspi->dev, "Flash CS is out of range.\n"); + return -EINVAL; + } + qspi->fnum++; + + + if (spi->max_speed_hz) { + clk_div = DIV_ROUND_UP(qspi->clk_rate, spi->max_speed_hz); + flash->clk_div = phytium_spi_nor_clac_clk_div(clk_div); + if (flash->clk_div == 65535) { + dev_err(qspi->dev, "qspi maximum frequency setting is error.\n"); + return -EINVAL; + } + } else + flash->clk_div = PHYTIUM_QSPI_DEFAULT_SCK_SEL; + + return 0; +} + +static struct spi_controller_mem_ops phytium_qspi_mem_ops = { + .adjust_op_size = phytium_qspi_adjust_op_size, + .supports_op = phytium_qspi_supports_op, + .exec_op = phytium_qspi_exec_op, + .dirmap_create = phytium_qspi_dirmap_create, + .dirmap_read = phytium_qspi_dirmap_read, + .dirmap_write = phytium_qspi_dirmap_write, +}; + +/** + * Direct mapping is supported only when all flashes under the controller + * are of the same size and the mapping address is continuous. For those + * cases which flashes are of different sizes, the driver offered a non-dirmap + * mem_ops with which read/write ops is executed through command port. + */ +static struct spi_controller_mem_ops phytium_qspi_mem_ops_nodirmap = { + .adjust_op_size = phytium_qspi_adjust_op_size, + .supports_op = phytium_qspi_supports_op, + .exec_op = phytium_qspi_exec_op, +}; + +/** + * phytium_qspi_probe - Probe method for the QSPI driver + * @pdev: Pointer to the platform_device structure + * + * This function initializes the driver data structures and the hardware. + * + * Return: 0 on success and error value on failure + */ +static int phytium_qspi_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct spi_controller *ctrl; + struct resource *res; + struct phytium_qspi *qspi; + int i, ret; + u32 flash_cap; + struct spi_mem *mem; + struct spi_nor *nor; + + ctrl = spi_alloc_master(dev, sizeof(*qspi)); + if (!ctrl) + return -ENOMEM; + + ctrl->mode_bits = SPI_CPOL | SPI_CPHA | + SPI_RX_DUAL | SPI_RX_QUAD | + SPI_TX_DUAL | SPI_TX_QUAD; + ctrl->setup = phytium_qspi_setup; + ctrl->num_chipselect = PHYTIUM_QSPI_MAX_NORCHIP; + ctrl->dev.of_node = dev->of_node; + + qspi = spi_controller_get_devdata(ctrl); + qspi->ctrl = ctrl; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qspi"); + qspi->io_base = devm_ioremap_resource(dev, res); + if (IS_ERR(qspi->io_base)) { + ret = PTR_ERR(qspi->io_base); + goto probe_master_put; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qspi_mm"); + qspi->mm_base = devm_ioremap_resource(dev, res); + if (IS_ERR(qspi->mm_base)) { + ret = PTR_ERR(qspi->mm_base); + goto probe_master_put; + } + + qspi->mm_size = resource_size(res); + if (qspi->mm_size > PHYTIUM_QSPI_MAX_MMAP_SZ) { + ret = -EINVAL; + goto probe_master_put; + } + qspi->used_size = 0; + + qspi->clk = devm_clk_get(dev, NULL); + if (IS_ERR(qspi->clk)) { + ret = PTR_ERR(qspi->clk); + goto probe_master_put; + } + + qspi->clk_rate = clk_get_rate(qspi->clk); + if (!qspi->clk_rate) { + ret = -EINVAL; + goto probe_master_put; + } + + pm_runtime_enable(dev); + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + pm_runtime_put_noidle(dev); + goto probe_master_put; + } + + ret = clk_prepare_enable(qspi->clk); + if (ret) { + dev_err(dev, "Failed to enable PCLK of the controller.\n"); + goto probe_clk_failed; + } + + qspi->nodirmap = device_property_present(dev, "no-direct-mapping"); + ctrl->mem_ops = qspi->nodirmap ? + &phytium_qspi_mem_ops_nodirmap : + &phytium_qspi_mem_ops; + + qspi->dev = dev; + platform_set_drvdata(pdev, qspi); + + ret = devm_spi_register_controller(dev, ctrl); + if (ret) { + dev_err(dev, "failed to register SPI controller: %d\n", ret); + goto probe_setup_failed; + } + + if (!qspi->nodirmap) { + /* + * The controller supports direct mapping access only if all + * flashes are of same size. + */ + + i = 0; + for (i = 0; qspi->fnum > i && i < PHYTIUM_QSPI_MAX_NORCHIP; i++) { + if (qspi->flash[i].spi) { + mem = spi_get_drvdata(qspi->flash[i].spi); + if (mem) { + nor = spi_mem_get_drvdata(mem); + if (nor) + qspi->flash[i].size = nor->mtd.size; + } + } + } + + for (i = 1; qspi->fnum > i && i < PHYTIUM_QSPI_MAX_NORCHIP; i++) { + if (qspi->flash[i].size != qspi->flash[0].size) { + dev_err(dev, "Flashes are of different sizes.\n"); + ret = -EINVAL; + goto probe_setup_failed; + } + } + + ret = phytium_qspi_flash_capacity_encode(qspi->flash[0].size, + &flash_cap); + if (ret) { + dev_err(dev, "Flash size is invalid.\n"); + goto probe_setup_failed; + } + + flash_cap |= qspi->fnum << QSPI_FLASH_CAP_NUM_SHIFT; + + writel_relaxed(flash_cap, qspi->io_base + QSPI_FLASH_CAP_REG); + } + + return 0; + +probe_setup_failed: + clk_disable_unprepare(qspi->clk); +probe_clk_failed: + pm_runtime_put_sync(dev); + pm_runtime_disable(dev); +probe_master_put: + + return ret; +} + +/** + * phytium_qspi_remove - Remove method for the QSPI driver + * @pdev: Pointer to the platform_device structure + * + * This function is called if a device is physically removed from the system + * or if the driver module is being unloaded. It free all resources allocated + * to the device. + * + * Return: 0 on success and error value on failure + */ +static int phytium_qspi_remove(struct platform_device *pdev) +{ + struct phytium_qspi *qspi = platform_get_drvdata(pdev); + + clk_disable_unprepare(qspi->clk); + + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static int __maybe_unused phytium_qspi_suspend(struct device *dev) +{ + return pm_runtime_force_suspend(dev); +} + +static int __maybe_unused phytium_qspi_resume(struct device *dev) +{ + return pm_runtime_force_resume(dev); +} + +static const struct dev_pm_ops phytium_qspi_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(phytium_qspi_suspend, + phytium_qspi_resume) +}; + +static const struct of_device_id phytium_qspi_of_match[] = { + { .compatible = "phytium,qspi-nor" }, + { } +}; +MODULE_DEVICE_TABLE(of, phytium_qspi_of_match); + +static struct platform_driver phytium_qspi_driver = { + .probe = phytium_qspi_probe, + .remove = phytium_qspi_remove, + .driver = { + .name = "phytium-qspi", + .of_match_table = of_match_ptr(phytium_qspi_of_match), + .pm = &phytium_qspi_pm_ops, + }, +}; +module_platform_driver(phytium_qspi_driver); + +MODULE_AUTHOR("Chen Baozi "); +MODULE_DESCRIPTION("Phytium Quad SPI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/target/linux/phytium/files-5.10/drivers/spi/spi-phytium.c b/target/linux/phytium/files-5.10/drivers/spi/spi-phytium.c new file mode 100644 index 000000000..a6f849c5c --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/spi/spi-phytium.c @@ -0,0 +1,510 @@ + // SPDX-License-Identifier: GPL-2.0 +/* + * Phytium SPI core controller driver. + * + * Copyright (c) 2019-2023 Phytium Technology Co., Ltd. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "spi-phytium.h" + +struct phytium_spi_chip { + u8 poll_mode; + u8 type; + void (*cs_control)(u32 command); +}; + +struct chip_data { + u8 cs; + u8 tmode; + u8 type; + + u8 poll_mode; + + u16 clk_div; + u32 speed_hz; + void (*cs_control)(u32 command); +}; + +static void phytium_spi_set_cs(struct spi_device *spi, bool enable) +{ + struct phytium_spi *fts = spi_master_get_devdata(spi->master); + struct chip_data *chip = spi_get_ctldata(spi); + u32 origin; + + if (chip && chip->cs_control) + chip->cs_control(!enable); + + if (!enable) { + phytium_writel(fts, SER, BIT(spi->chip_select)); + if (fts->global_cs) { + origin = phytium_readl(fts, GCSR); + phytium_writel(fts, GCSR, origin | (1 << spi->chip_select)); + } + } else { + if (fts->global_cs) { + origin = phytium_readl(fts, GCSR); + phytium_writel(fts, GCSR, origin & ~(1 << spi->chip_select)); + } + } +} + +static inline u32 tx_max(struct phytium_spi *fts) +{ + u32 tx_left, tx_room, rxtx_gap; + + tx_left = (fts->tx_end - fts->tx) / fts->n_bytes; + tx_room = fts->fifo_len - phytium_readl(fts, TXFLR); + + rxtx_gap = ((fts->rx_end - fts->rx) - (fts->tx_end - fts->tx)) + / fts->n_bytes; + + return min3(tx_left, tx_room, (u32) (fts->fifo_len - rxtx_gap)); +} + +static inline u32 rx_max(struct phytium_spi *fts) +{ + u32 rx_left = (fts->rx_end - fts->rx) / fts->n_bytes; + + return min_t(u32, rx_left, phytium_readl(fts, RXFLR)); +} + +static void phytium_writer(struct phytium_spi *fts) +{ + u32 max = tx_max(fts); + u16 txw = 0; + + while (max--) { + if (fts->tx_end - fts->len) { + if (fts->n_bytes == 1) + txw = *(u8 *)(fts->tx); + else + txw = *(u16 *)(fts->tx); + } + phytium_write_io_reg(fts, DR, txw); + fts->tx += fts->n_bytes; + } +} + +static void phytium_reader(struct phytium_spi *fts) +{ + u32 max = rx_max(fts); + u16 rxw; + + while (max--) { + rxw = phytium_read_io_reg(fts, DR); + if (fts->rx_end - fts->len) { + if (fts->n_bytes == 1) + *(u8 *)(fts->rx) = rxw; + else + *(u16 *)(fts->rx) = rxw; + } + fts->rx += fts->n_bytes; + } +} + +int phytium_spi_check_status(struct phytium_spi *fts, bool raw) +{ + u32 irq_status; + int ret = 0; + + if (raw) + irq_status = phytium_readl(fts, RISR); + else + irq_status = phytium_readl(fts, ISR); + + if (irq_status & INT_RXOI) { + dev_err(&fts->master->dev, "RX FIFO overflow detected\n"); + ret = -EIO; + } + + if (irq_status & INT_RXUI) { + dev_err(&fts->master->dev, "RX FIFO underflow detected\n"); + ret = -EIO; + } + + if (irq_status & INT_TXOI) { + dev_err(&fts->master->dev, "TX FIFO overflow detected\n"); + ret = -EIO; + } + + /* Generically handle the erroneous situation */ + if (ret) { + spi_reset_chip(fts); + if (fts->master->cur_msg) + fts->master->cur_msg->status = ret; + } + + return ret; +} +EXPORT_SYMBOL_GPL(phytium_spi_check_status); + +static void int_error_stop(struct phytium_spi *fts, const char *msg) +{ + spi_reset_chip(fts); + + dev_err(&fts->master->dev, "%s\n", msg); + fts->master->cur_msg->status = -EIO; + spi_finalize_current_transfer(fts->master); +} + +static irqreturn_t interrupt_transfer(struct phytium_spi *fts) +{ + u16 irq_status = phytium_readl(fts, ISR); + + if (irq_status & (INT_TXOI | INT_RXOI | INT_RXUI)) { + phytium_readl(fts, ICR); + int_error_stop(fts, "interrupt_transfer: fifo overrun/underrun"); + return IRQ_HANDLED; + } + + phytium_reader(fts); + if (fts->rx_end == fts->rx) { + spi_mask_intr(fts, INT_TXEI); + spi_finalize_current_transfer(fts->master); + return IRQ_HANDLED; + } + if (irq_status & INT_TXEI) { + spi_mask_intr(fts, INT_TXEI); + phytium_writer(fts); + spi_umask_intr(fts, INT_TXEI); + } + + return IRQ_HANDLED; +} + +static irqreturn_t phytium_spi_irq(int irq, void *dev_id) +{ + struct spi_master *master = dev_id; + struct phytium_spi *fts = spi_master_get_devdata(master); + u16 irq_status = phytium_readl(fts, ISR) & 0x3f; + + if (!irq_status) + return IRQ_NONE; + + if (!master->cur_msg) { + spi_mask_intr(fts, INT_TXEI); + return IRQ_HANDLED; + } + + if (fts->transfer_handler) + return fts->transfer_handler(fts); + else + return IRQ_HANDLED; +} + +static int poll_transfer(struct phytium_spi *fts) +{ + do { + phytium_writer(fts); + phytium_reader(fts); + cpu_relax(); + } while (fts->rx_end > fts->rx); + + return 0; +} + +static int phytium_spi_transfer_one(struct spi_master *master, + struct spi_device *spi, struct spi_transfer *transfer) +{ + struct phytium_spi *fts = spi_master_get_devdata(master); + struct chip_data *chip = spi_get_ctldata(spi); + u8 imask = 0; + u16 txlevel = 0; + u16 clk_div; + u32 cr0; + int ret = 0; + + fts->dma_mapped = 0; + fts->tx = (void *)transfer->tx_buf; + fts->tx_end = fts->tx + transfer->len; + fts->rx = transfer->rx_buf; + fts->rx_end = fts->rx + transfer->len; + fts->len = transfer->len; + + spi_enable_chip(fts, 0); + + if (transfer->speed_hz != fts->current_freq) { + if (transfer->speed_hz != chip->speed_hz) { + clk_div = (fts->max_freq / transfer->speed_hz + 1) & + 0xfffe; + + chip->speed_hz = transfer->speed_hz; + chip->clk_div = clk_div; + } + fts->current_freq = transfer->speed_hz; + spi_set_clk(fts, chip->clk_div); + } + + if (transfer->bits_per_word == 8) { + fts->n_bytes = 1; + } else if (transfer->bits_per_word == 16) { + fts->n_bytes = 2; + } else { + return -EINVAL; + } + + cr0 = (transfer->bits_per_word - 1) + | (chip->type << FRF_OFFSET) + | (spi->mode << MODE_OFFSET) + | (chip->tmode << TMOD_OFFSET); + + if (chip->cs_control) { + if (fts->rx && fts->tx) + chip->tmode = TMOD_TR; + else if (fts->rx) + chip->tmode = TMOD_RO; + else + chip->tmode = TMOD_TO; + + cr0 &= ~TMOD_MASK; + cr0 |= (chip->tmode << TMOD_OFFSET); + } + + phytium_writel(fts, CTRLR0, cr0); + + /* check if current transfer is a DMA transcation */ + if (master->can_dma && master->can_dma(master, spi, transfer)) + fts->dma_mapped = master->cur_msg_mapped; + + spi_mask_intr(fts, 0xff); + + /* DMA setup */ + if (fts->dma_mapped) { + ret = fts->dma_ops->dma_setup(fts, transfer); + if (ret) + return ret; + } + + /* interrupt transfer mode setup */ + if (!chip->poll_mode && !fts->dma_mapped) { + txlevel = min_t(u16, fts->fifo_len / 2, fts->len / fts->n_bytes); + phytium_writel(fts, TXFLTR, txlevel); + + imask |= INT_TXEI | INT_TXOI | + INT_RXUI | INT_RXOI; + spi_umask_intr(fts, imask); + + fts->transfer_handler = interrupt_transfer; + } + + spi_enable_chip(fts, 1); + + if (fts->dma_mapped) + return fts->dma_ops->dma_transfer(fts, transfer); + + if (chip->poll_mode) + return poll_transfer(fts); + + return 1; +} + +static void phytium_spi_handle_err(struct spi_master *master, + struct spi_message *msg) +{ + struct phytium_spi *fts = spi_master_get_devdata(master); + + if (fts->dma_mapped) + fts->dma_ops->dma_stop(fts); + + spi_reset_chip(fts); +} + +static int phytium_spi_setup(struct spi_device *spi) +{ + struct phytium_spi_chip *chip_info = NULL; + struct chip_data *chip; + struct spi_master *master = spi->master; + struct phytium_spi *fts = spi_master_get_devdata(master); + int ret; + u32 cr0; + + spi_enable_chip(fts, 0); + + chip = spi_get_ctldata(spi); + if (!chip) { + chip = kzalloc(sizeof(struct chip_data), GFP_KERNEL); + if (!chip) + return -ENOMEM; + spi_set_ctldata(spi, chip); + } + + chip_info = spi->controller_data; + + if (chip_info) { + if (chip_info->cs_control) + chip->cs_control = chip_info->cs_control; + + chip->poll_mode = chip_info->poll_mode; + chip->type = chip_info->type; + } + + chip->tmode = 0; + + cr0 = (spi->bits_per_word - 1) | (chip->type << FRF_OFFSET) | + (spi->mode << MODE_OFFSET) | (chip->tmode << TMOD_OFFSET); + + phytium_writel(fts, CTRLR0, cr0); + + if (gpio_is_valid(spi->cs_gpio)) { + ret = gpio_direction_output(spi->cs_gpio, + !(spi->mode & SPI_CS_HIGH)); + if (ret) + return ret; + } + + spi_enable_chip(fts, 1); + + return 0; +} + +static void phytium_spi_cleanup(struct spi_device *spi) +{ + struct chip_data *chip = spi_get_ctldata(spi); + + kfree(chip); + spi_set_ctldata(spi, NULL); +} + +static void spi_hw_init(struct device *dev, struct phytium_spi *fts) +{ + spi_reset_chip(fts); + + if (!fts->fifo_len) { + u32 fifo; + + for (fifo = 1; fifo < 256; fifo++) { + phytium_writel(fts, TXFLTR, fifo); + if (fifo != phytium_readl(fts, TXFLTR)) + break; + } + phytium_writel(fts, TXFLTR, 0); + + fts->fifo_len = (fifo == 1) ? 0 : fifo; + dev_dbg(dev, "Detected FIFO size: %u bytes\n", fts->fifo_len); + } +} + +int phytium_spi_add_host(struct device *dev, struct phytium_spi *fts) +{ + struct spi_master *master; + int ret; + + BUG_ON(fts == NULL); + + master = spi_alloc_master(dev, 0); + if (!master) + return -ENOMEM; + + fts->master = master; + fts->dma_addr = (dma_addr_t)(fts->paddr + DR); + snprintf(fts->name, sizeof(fts->name), "phytium_spi%d", fts->bus_num); + + ret = request_irq(fts->irq, phytium_spi_irq, IRQF_SHARED, fts->name, master); + if (ret < 0) { + dev_err(dev, "can not get IRQ\n"); + goto err_free_master; + } + + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP; + master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16); + master->bus_num = fts->bus_num; + master->num_chipselect = fts->num_cs; + master->setup = phytium_spi_setup; + master->cleanup = phytium_spi_cleanup; + master->set_cs = phytium_spi_set_cs; + master->transfer_one = phytium_spi_transfer_one; + master->handle_err = phytium_spi_handle_err; + master->max_speed_hz = fts->max_freq; + master->dev.of_node = dev->of_node; + master->dev.fwnode = dev->fwnode; + master->flags = SPI_MASTER_GPIO_SS; + master->cs_gpios = fts->cs; + + spi_hw_init(dev, fts); + + + if (fts->dma_ops && fts->dma_ops->dma_init) { + ret = fts->dma_ops->dma_init(dev, fts); + if (ret) { + dev_warn(dev, "DMA init failed\n"); + } else { + master->can_dma = fts->dma_ops->can_dma; + master->flags |= SPI_CONTROLLER_MUST_TX; + } + } + + spi_master_set_devdata(master, fts); + ret = devm_spi_register_master(dev, master); + if (ret) { + dev_err(&master->dev, "problem registering spi master\n"); + goto err_exit; + } + + return 0; + +err_exit: + if (fts->dma_ops && fts->dma_ops->dma_exit) + fts->dma_ops->dma_exit(fts); + spi_enable_chip(fts, 0); + free_irq(fts->irq, master); +err_free_master: + spi_master_put(master); + return ret; +} +EXPORT_SYMBOL_GPL(phytium_spi_add_host); + +void phytium_spi_remove_host(struct phytium_spi *fts) +{ + if (fts->dma_ops && fts->dma_ops->dma_exit) + fts->dma_ops->dma_exit(fts); + spi_shutdown_chip(fts); + + free_irq(fts->irq, fts->master); +} +EXPORT_SYMBOL_GPL(phytium_spi_remove_host); + +int phytium_spi_suspend_host(struct phytium_spi *fts) +{ + int ret; + + ret = spi_controller_suspend(fts->master); + if (ret) + return ret; + + spi_shutdown_chip(fts); + return 0; +} +EXPORT_SYMBOL_GPL(phytium_spi_suspend_host); + +int phytium_spi_resume_host(struct phytium_spi *fts) +{ + int ret; + + spi_hw_init(&fts->master->dev, fts); + ret = spi_controller_resume(fts->master); + if (ret) + dev_err(&fts->master->dev, "fail to start queue (%d)\n", ret); + return ret; +} +EXPORT_SYMBOL_GPL(phytium_spi_resume_host); + +MODULE_AUTHOR("Zhu Mingshuai "); +MODULE_AUTHOR("Chen Baozi "); +MODULE_DESCRIPTION("Driver for Phytium SPI controller core"); +MODULE_LICENSE("GPL v2"); diff --git a/target/linux/phytium/files-5.10/drivers/spi/spi-phytium.h b/target/linux/phytium/files-5.10/drivers/spi/spi-phytium.h new file mode 100644 index 000000000..003b08f8c --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/spi/spi-phytium.h @@ -0,0 +1,213 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef PHYTIUM_SPI_HEADER_H +#define PHYTIUM_SPI_HEADER_H + +#include +#include +#include + +#define CTRLR0 0x00 +#define SSIENR 0x08 +#define SER 0x10 +#define BAUDR 0x14 +#define TXFLTR 0x18 +#define TXFLR 0x20 +#define RXFLR 0x24 +#define SR 0x28 +#define IMR 0x2c +#define ISR 0x30 +#define RISR 0x34 +#define ICR 0x48 +#define DMACR 0x4C +#define DMATDLR 0x50 +#define DMARDLR 0x54 +#define DR 0x60 +#define GCSR 0x100 + +#define FRF_OFFSET 4 +#define MODE_OFFSET 6 +#define TMOD_OFFSET 8 + +#define TMOD_MASK (0x3 << TMOD_OFFSET) +#define TMOD_TR 0x0 +#define TMOD_TO 0x1 +#define TMOD_RO 0x2 + +#define INT_TXEI (1 << 0) +#define INT_TXOI (1 << 1) +#define INT_RXUI (1 << 2) +#define INT_RXOI (1 << 3) + +/* Bit fields in SR, 7 bits */ +#define SR_MASK 0x7f /* cover 7 bits */ +#define SR_BUSY (1 << 0) +#define SR_TF_NOT_FULL (1 << 1) +#define SR_TF_EMPT (1 << 2) +#define SR_RF_NOT_EMPT (1 << 3) +#define SR_RF_FULL (1 << 4) +#define SR_TX_ERR (1 << 5) +#define SR_DCOL (1 << 6) + +/* Bit fields in DMACR */ +#define SPI_DMA_RDMAE (1 << 0) +#define SPI_DMA_TDMAE (1 << 1) + +#define SPI_WAIT_RETRIES 5 + +struct phytium_spi; + +struct phytium_spi_dma_ops { + int (*dma_init)(struct device *dev, struct phytium_spi *fts); + void (*dma_exit)(struct phytium_spi *fts); + int (*dma_setup)(struct phytium_spi *fts, struct spi_transfer *xfer); + bool (*can_dma)(struct spi_controller *master, struct spi_device *spi, + struct spi_transfer *xfer); + int (*dma_transfer)(struct phytium_spi *fts, struct spi_transfer *xfer); + void (*dma_stop)(struct phytium_spi *fts); +}; + +struct phytium_spi { + struct spi_master *master; + char name[16]; + + void __iomem *regs; + bool global_cs; + bool dma_en; + unsigned long paddr; + int irq; + u32 fifo_len; + u32 max_freq; + + u32 reg_io_width; + u16 bus_num; + u16 num_cs; + int *cs; + + size_t len; + void *tx; + void *tx_end; + void *rx; + void *rx_end; + u8 n_bytes; + int dma_mapped; + struct clk *clk; + irqreturn_t (*transfer_handler)(struct phytium_spi *fts); + u32 current_freq; /* frequency in hz */ + + /* DMA info */ + struct dma_chan *txchan; + u32 txburst; + struct dma_chan *rxchan; + u32 rxburst; + u32 dma_sg_burst; + unsigned long dma_chan_busy; + dma_addr_t dma_addr; /* phy address of the Data register */ + const struct phytium_spi_dma_ops *dma_ops; + struct completion dma_completion; +}; + +static inline u32 phytium_readl(struct phytium_spi *fts, u32 offset) +{ + return __raw_readl(fts->regs + offset); +} + +static inline u16 phytium_readw(struct phytium_spi *fts, u32 offset) +{ + return __raw_readw(fts->regs + offset); +} + +static inline void phytium_writel(struct phytium_spi *fts, u32 offset, u32 val) +{ + __raw_writel(val, fts->regs + offset); +} + +static inline void phytium_writew(struct phytium_spi *fts, u32 offset, u16 val) +{ + __raw_writew(val, fts->regs + offset); +} + +static inline u32 phytium_read_io_reg(struct phytium_spi *fts, u32 offset) +{ + switch (fts->reg_io_width) { + case 2: + return phytium_readw(fts, offset); + case 4: + default: + return phytium_readl(fts, offset); + } +} + +static inline void phytium_write_io_reg(struct phytium_spi *fts, u32 offset, u32 val) +{ + switch (fts->reg_io_width) { + case 2: + phytium_writew(fts, offset, val); + break; + case 4: + default: + phytium_writel(fts, offset, val); + break; + } +} + +static inline void spi_enable_chip(struct phytium_spi *fts, int enable) +{ + phytium_writel(fts, SSIENR, (enable ? 1 : 0)); +} + +static inline void spi_set_clk(struct phytium_spi *fts, u16 div) +{ + phytium_writel(fts, BAUDR, div); +} + +static inline void spi_mask_intr(struct phytium_spi *fts, u32 mask) +{ + u32 new_mask; + + new_mask = phytium_readl(fts, IMR) & ~mask; + phytium_writel(fts, IMR, new_mask); +} + +static inline void spi_umask_intr(struct phytium_spi *fts, u32 mask) +{ + u32 new_mask; + + new_mask = phytium_readl(fts, IMR) | mask; + phytium_writel(fts, IMR, new_mask); +} + +static inline void spi_global_cs(struct phytium_spi *fts) +{ + u32 global_cs_en, mask, setmask; + + mask = GENMASK(fts->num_cs-1, 0) << fts->num_cs; + setmask = ~GENMASK(fts->num_cs-1, 0); + global_cs_en = (phytium_readl(fts, GCSR) | mask) & setmask; + + phytium_writel(fts, GCSR, global_cs_en); +} + +static inline void spi_reset_chip(struct phytium_spi *fts) +{ + spi_enable_chip(fts, 0); + if (fts->global_cs) + spi_global_cs(fts); + spi_mask_intr(fts, 0xff); + spi_enable_chip(fts, 1); +} + +static inline void spi_shutdown_chip(struct phytium_spi *fts) +{ + spi_enable_chip(fts, 0); + spi_set_clk(fts, 0); + fts->current_freq = 0; +} + +extern int phytium_spi_add_host(struct device *dev, struct phytium_spi *fts); +extern void phytium_spi_remove_host(struct phytium_spi *fts); +extern int phytium_spi_suspend_host(struct phytium_spi *fts); +extern int phytium_spi_resume_host(struct phytium_spi *fts); +extern void phytium_spi_dmaops_set(struct phytium_spi *fts); +extern int phytium_spi_check_status(struct phytium_spi *fts, bool raw); + +#endif /* PHYTIUM_SPI_HEADER_H */ diff --git a/target/linux/phytium/files-5.10/drivers/tty/serial/phytium-uart.c b/target/linux/phytium/files-5.10/drivers/tty/serial/phytium-uart.c new file mode 100644 index 000000000..084bf9e1f --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/tty/serial/phytium-uart.c @@ -0,0 +1,922 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for Phytium PCI UART controller + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "phytium_uart" + +#define REG_DR 0x00 +#define REG_FR 0x18 +#define REG_IBRD 0x24 +#define REG_FBRD 0x28 +#define REG_LCRH_RX 0x2c +#define REG_LCRH_TX 0x2c +#define REG_CR 0x30 +#define REG_IFLS 0x34 +#define REG_IMSC 0x38 +#define REG_RIS 0x3c +#define REG_MIS 0x40 +#define REG_ICR 0x44 + +#define REG_DR_OE (1 << 11) +#define REG_DR_BE (1 << 10) +#define REG_DR_PE (1 << 9) +#define REG_DR_FE (1 << 8) + +#define REG_LCRH_SPS 0x80 +#define REG_LCRH_WLEN_8 0x60 +#define REG_LCRH_WLEN_7 0x40 +#define REG_LCRH_WLEN_6 0x20 +#define REG_LCRH_WLEN_5 0x00 +#define REG_LCRH_FEN 0x10 +#define REG_LCRH_STP2 0x08 +#define REG_LCRH_EPS 0x04 +#define REG_LCRH_PEN 0x02 +#define REG_LCRH_BRK 0x01 + +#define REG_FR_RI 0x100 +#define REG_FR_TXFE 0x080 +#define REG_FR_RXFF 0x040 +#define REG_FR_TXFF 0x020 +#define REG_FR_RXFE 0x010 +#define REG_FR_BUSY 0x008 +#define REG_FR_DCD 0x004 +#define REG_FR_DSR 0x002 +#define REG_FR_CTS 0x001 +#define REG_FR_TMSK (REG_FR_TXFF + REG_FR_BUSY) + +#define REG_CR_CTSEN 0x8000 /* CTS hardware flow control */ +#define REG_CR_RTSEN 0x4000 /* RTS hardware flow control */ +#define REG_CR_OUT2 0x2000 /* OUT2 */ +#define REG_CR_OUT1 0x1000 /* OUT1 */ +#define REG_CR_RTS 0x0800 /* RTS */ +#define REG_CR_DTR 0x0400 /* DTR */ +#define REG_CR_RXE 0x0200 /* receive enable */ +#define REG_CR_TXE 0x0100 /* transmit enable */ +#define REG_CR_LBE 0x0080 /* loopback enable */ +#define REG_CR_RTIE 0x0040 +#define REG_CR_TIE 0x0020 +#define REG_CR_RIE 0x0010 +#define REG_CR_MSIE 0x0008 +#define REG_CR_IIRLP 0x0004 /* SIR low power mode */ +#define REG_CR_SIREN 0x0002 /* SIR enable */ +#define REG_CR_UARTEN 0x0001 /* UART enable */ + +#define REG_IFLS_RX1_8 (0 << 3) +#define REG_IFLS_RX2_8 (1 << 3) +#define REG_IFLS_RX4_8 (2 << 3) +#define REG_IFLS_RX6_8 (3 << 3) +#define REG_IFLS_RX7_8 (4 << 3) +#define REG_IFLS_TX1_8 (0 << 0) +#define REG_IFLS_TX2_8 (1 << 0) +#define REG_IFLS_TX4_8 (2 << 0) +#define REG_IFLS_TX6_8 (3 << 0) + +#define REG_IMSC_OEIM (1 << 10) /* overrun error interrupt mask */ +#define REG_IMSC_BEIM (1 << 9) /* break error interrupt mask */ +#define REG_IMSC_PEIM (1 << 8) /* parity error interrupt mask */ +#define REG_IMSC_FEIM (1 << 7) /* framing error interrupt mask */ +#define REG_IMSC_RTIM (1 << 6) /* receive timeout interrupt mask */ +#define REG_IMSC_TXIM (1 << 5) /* transmit interrupt mask */ +#define REG_IMSC_RXIM (1 << 4) /* receive interrupt mask */ +#define REG_IMSC_DSRMIM (1 << 3) /* DSR interrupt mask */ +#define REG_IMSC_DCDMIM (1 << 2) /* DCD interrupt mask */ +#define REG_IMSC_CTSMIM (1 << 1) /* CTS interrupt mask */ +#define REG_IMSC_RIMIM (1 << 0) /* RI interrupt mask */ + +#define REG_ICR_OEIS (1 << 10) /* overrun error interrupt status */ +#define REG_ICR_BEIS (1 << 9) /* break error interrupt status */ +#define REG_ICR_PEIS (1 << 8) /* parity error interrupt status */ +#define REG_ICR_FEIS (1 << 7) /* framing error interrupt status */ +#define REG_ICR_RTIS (1 << 6) /* receive timeout interrupt status */ +#define REG_ICR_TXIS (1 << 5) /* transmit interrupt status */ +#define REG_ICR_RXIS (1 << 4) /* receive interrupt status */ +#define REG_ICR_DSRMIS (1 << 3) /* DSR interrupt status */ +#define REG_ICR_DCDMIS (1 << 2) /* DCD interrupt status */ +#define REG_ICR_CTSMIS (1 << 1) /* CTS interrupt status */ +#define REG_ICR_RIMIS (1 << 0) /* RI interrupt status */ + +#define UART_NR 12 + +#define UART_DR_ERROR (REG_DR_OE|REG_DR_BE|REG_DR_PE|REG_DR_FE) +#define UART_DUMMY_DR_RX (1 << 16) + +#define DEFAULT_UARTCLK 48000000 /* 48 MHz */ + +/* + * We wrap our port structure around the generic uart_port. + */ +struct phytium_uart_port { + struct uart_port port; + unsigned int im; /* interrupt mask */ + unsigned int old_status; + unsigned int old_cr; /* state during shutdown */ + char type[12]; +}; + +static unsigned int phytium_uart_read(const struct phytium_uart_port *pup, + unsigned int reg) +{ + void __iomem *addr = pup->port.membase + reg; + + return readl_relaxed(addr); +} + +static void phytium_uart_write(unsigned int val, const struct phytium_uart_port *pup, + unsigned int reg) +{ + void __iomem *addr = pup->port.membase + reg; + + writel_relaxed(val, addr); +} + +static int phytium_fifo_to_tty(struct phytium_uart_port *pup) +{ + u16 status; + unsigned int ch, flag, fifotaken; + + for (fifotaken = 0; fifotaken < 256; fifotaken++) { + status = phytium_uart_read(pup, REG_FR); + if (status & REG_FR_RXFE) + break; + + /* Take chars from the FIFO and update status */ + ch = phytium_uart_read(pup, REG_DR) | UART_DUMMY_DR_RX; + flag = TTY_NORMAL; + pup->port.icount.rx++; + + if (unlikely(ch & UART_DR_ERROR)) { + if (ch & REG_DR_BE) { + ch &= ~(REG_DR_FE | REG_DR_PE); + pup->port.icount.brk++; + if (uart_handle_break(&pup->port)) + continue; + } else if (ch & REG_DR_PE) + pup->port.icount.parity++; + else if (ch & REG_DR_FE) + pup->port.icount.frame++; + if (ch & REG_DR_OE) + pup->port.icount.overrun++; + + ch &= pup->port.read_status_mask; + + if (ch & REG_DR_BE) + flag = TTY_BREAK; + else if (ch & REG_DR_PE) + flag = TTY_PARITY; + else if (ch & REG_DR_FE) + flag = TTY_FRAME; + } + + if (uart_handle_sysrq_char(&pup->port, ch & 255)) + continue; + + uart_insert_char(&pup->port, ch, REG_DR_OE, ch, flag); + } + + return fifotaken; +} + +static void phytium_rx_chars(struct phytium_uart_port *pup) +__releases(&pup->port.lock) +__acquires(&pup->port.lock) +{ + phytium_fifo_to_tty(pup); + + spin_unlock(&pup->port.lock); + tty_flip_buffer_push(&pup->port.state->port); + spin_lock(&pup->port.lock); +} + +static void phytium_stop_tx(struct uart_port *port) +{ + struct phytium_uart_port *pup = + container_of(port, struct phytium_uart_port, port); + + pup->im &= ~REG_IMSC_TXIM; + phytium_uart_write(pup->im, pup, REG_IMSC); +} + +static bool phytium_tx_char(struct phytium_uart_port *pup, unsigned char c, + bool from_irq) +{ + + if (unlikely(!from_irq) && + phytium_uart_read(pup, REG_FR) & REG_FR_TXFF) + return false; /* unable to transmit character */ + + phytium_uart_write(c, pup, REG_DR); + pup->port.icount.tx++; + + return true; +} + +static bool phytium_tx_chars(struct phytium_uart_port *pup, bool from_irq) +{ + struct circ_buf *xmit = &pup->port.state->xmit; + int count = pup->port.fifosize >> 1; + + if (pup->port.x_char) { + if (!phytium_tx_char(pup, pup->port.x_char, from_irq)) + return true; + pup->port.x_char = 0; + --count; + } + if (uart_circ_empty(xmit) || uart_tx_stopped(&pup->port)) { + phytium_stop_tx(&pup->port); + return false; + } + + do { + if (likely(from_irq) && count-- == 0) + break; + + if (!phytium_tx_char(pup, xmit->buf[xmit->tail], from_irq)) + break; + + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + } while (!uart_circ_empty(xmit)); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&pup->port); + + if (uart_circ_empty(xmit)) { + phytium_stop_tx(&pup->port); + return false; + } + return true; +} + +static void phytium_modem_status(struct phytium_uart_port *pup) +{ + unsigned int status, delta; + + status = phytium_uart_read(pup, REG_FR) & (REG_FR_DCD|REG_FR_DSR|REG_FR_CTS); + + delta = status ^ pup->old_status; + pup->old_status = status; + + if (!delta) + return; + + if (delta & REG_FR_DCD) + uart_handle_dcd_change(&pup->port, status & REG_FR_DCD); + + if (delta & REG_FR_DSR) + pup->port.icount.dsr++; + + if (delta & REG_FR_CTS) + uart_handle_cts_change(&pup->port, status & REG_FR_CTS); + + wake_up_interruptible(&pup->port.state->port.delta_msr_wait); +} + +static irqreturn_t phytium_uart_interrupt(int irq, void *dev_id) +{ + struct phytium_uart_port *pup = dev_id; + unsigned long flags; + unsigned int status, pass_counter = 256; + int handled = 0; + + spin_lock_irqsave(&pup->port.lock, flags); + status = phytium_uart_read(pup, REG_RIS) & pup->im; + if (status) { + do { + phytium_uart_write(status & ~(REG_ICR_TXIS|REG_ICR_RTIS|REG_ICR_RXIS), + pup, REG_ICR); + + if (status & (REG_ICR_RTIS|REG_ICR_RXIS)) + phytium_rx_chars(pup); + + if (status & (REG_ICR_DSRMIS|REG_ICR_DCDMIS| + REG_ICR_CTSMIS|REG_ICR_RIMIS)) + phytium_modem_status(pup); + if (status & REG_ICR_TXIS) + phytium_tx_chars(pup, true); + + if (pass_counter-- == 0) + break; + + status = phytium_uart_read(pup, REG_RIS) & pup->im; + } while (status != 0); + handled = 1; + } + spin_unlock_irqrestore(&pup->port.lock, flags); + + return IRQ_RETVAL(handled); +} + +static unsigned int phytium_tx_empty(struct uart_port *port) +{ + unsigned int status; + struct phytium_uart_port *pup = + container_of(port, struct phytium_uart_port, port); + + status = phytium_uart_read(pup, REG_FR) & (REG_FR_BUSY | REG_FR_TXFF); + + return status ? 0 : TIOCSER_TEMT; +} + +static void phytium_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct phytium_uart_port *pup = + container_of(port, struct phytium_uart_port, port); + unsigned int cr; + + cr = phytium_uart_read(pup, REG_CR); + +#define TIOCMBIT(tiocmbit, uartbit) \ + do { \ + if (mctrl & tiocmbit) \ + cr |= uartbit; \ + else \ + cr &= ~uartbit; \ + } while (0) + + TIOCMBIT(TIOCM_RTS, REG_CR_RTS); + TIOCMBIT(TIOCM_DTR, REG_CR_DTR); + TIOCMBIT(TIOCM_OUT1, REG_CR_OUT1); + TIOCMBIT(TIOCM_OUT2, REG_CR_OUT2); + TIOCMBIT(TIOCM_LOOP, REG_CR_LBE); + + if (port->status & UPSTAT_AUTORTS) { + /* We need to disable auto-RTS if we want to turn RTS off */ + TIOCMBIT(TIOCM_RTS, REG_CR_RTSEN); + } +#undef TIOCMBIT + + phytium_uart_write(cr, pup, REG_CR); +} + +static unsigned int phytium_get_mctrl(struct uart_port *port) +{ + struct phytium_uart_port *pup = + container_of(port, struct phytium_uart_port, port); + unsigned int cr = 0; + unsigned int status = phytium_uart_read(pup, REG_FR); + +#define TIOCMBIT(uartbit, tiocmbit) \ + do { \ + if (status & uartbit) \ + cr |= tiocmbit; \ + } while (0) + + TIOCMBIT(REG_FR_DCD, TIOCM_CAR); + TIOCMBIT(REG_FR_DSR, TIOCM_DSR); + TIOCMBIT(REG_FR_CTS, TIOCM_CTS); + TIOCMBIT(REG_FR_RI, TIOCM_RNG); +#undef TIOCMBIT + return cr; +} + +static void phytium_start_tx(struct uart_port *port) +{ + struct phytium_uart_port *pup = + container_of(port, struct phytium_uart_port, port); + + if (phytium_tx_chars(pup, false)) { + pup->im |= REG_IMSC_TXIM; + phytium_uart_write(pup->im, pup, REG_IMSC); + } +} + +static void phytium_stop_rx(struct uart_port *port) +{ + struct phytium_uart_port *pup = + container_of(port, struct phytium_uart_port, port); + + pup->im &= ~(REG_IMSC_RXIM|REG_IMSC_RTIM|REG_IMSC_FEIM| + REG_IMSC_PEIM|REG_IMSC_BEIM|REG_IMSC_OEIM); + phytium_uart_write(pup->im, pup, REG_IMSC); +} + +static void phytium_enable_ms(struct uart_port *port) +{ + struct phytium_uart_port *pup = + container_of(port, struct phytium_uart_port, port); + + pup->im |= REG_IMSC_RIMIM|REG_IMSC_CTSMIM|REG_IMSC_DCDMIM|REG_IMSC_DSRMIM; + phytium_uart_write(pup->im, pup, REG_IMSC); +} + +static void phytium_break_ctl(struct uart_port *port, int break_state) +{ + struct phytium_uart_port *pup = + container_of(port, struct phytium_uart_port, port); + unsigned long flags; + unsigned int lcr_h; + + spin_lock_irqsave(&pup->port.lock, flags); + lcr_h = phytium_uart_read(pup, REG_LCRH_TX); + if (break_state == -1) + lcr_h |= REG_LCRH_BRK; + else + lcr_h &= ~REG_LCRH_BRK; + phytium_uart_write(lcr_h, pup, REG_LCRH_TX); + spin_unlock_irqrestore(&pup->port.lock, flags); +} + +static int phytium_hwinit(struct uart_port *port) +{ + struct phytium_uart_port *pup = + container_of(port, struct phytium_uart_port, port); + + /* XXX: more configurable setup method in future */ + pup->port.uartclk = DEFAULT_UARTCLK; + + /* Clear pending error and receive interrupts */ + phytium_uart_write(REG_ICR_OEIS | REG_ICR_BEIS | REG_ICR_PEIS | + REG_ICR_FEIS | REG_ICR_RTIS | REG_ICR_RXIS, + pup, REG_ICR); + + /* + * Save interrupts enable mask, and enable RX interrupts in case if + * the interrupt is used for NMI entry. + */ + pup->im = phytium_uart_read(pup, REG_IMSC); + phytium_uart_write(REG_IMSC_RTIM | REG_IMSC_RXIM, pup, REG_IMSC); + + return 0; +} + +static int phytium_uart_allocate_irq(struct phytium_uart_port *pup) +{ + phytium_uart_write(pup->im, pup, REG_IMSC); + + return request_irq(pup->port.irq, phytium_uart_interrupt, IRQF_SHARED, DRV_NAME, pup); +} + +static void phytium_enable_interrtups(struct phytium_uart_port *pup) +{ + unsigned int i; + + spin_lock_irq(&pup->port.lock); + + /* Clear out any spuriously appearing RX interrupts */ + phytium_uart_write(REG_ICR_RTIS | REG_ICR_RXIS, pup, REG_ICR); + + /* + * RXIS is asserted only when the RX FIFO transitions from below + * to above the trigger threshold. If the RX FIFO is already + * full to the threashold this can't happen and RXIS will now be + * stuck off. Drain the RX FIFO explicitly to fix this: + */ + for (i = 0; i < pup->port.fifosize * 2; i++) { + if (phytium_uart_read(pup, REG_FR) & REG_FR_RXFE) + break; + + phytium_uart_read(pup, REG_DR); + } + + pup->im = REG_IMSC_RTIM | REG_IMSC_RXIM; + phytium_uart_write(pup->im, pup, REG_IMSC); + spin_unlock_irq(&pup->port.lock); +} + +static int phytium_startup(struct uart_port *port) +{ + struct phytium_uart_port *pup = + container_of(port, struct phytium_uart_port, port); + unsigned int cr; + int ret = 0; + + ret = phytium_hwinit(port); + if (ret) + goto out; + + ret = phytium_uart_allocate_irq(pup); + if (ret) + goto out; + + phytium_uart_write(REG_IFLS_RX4_8|REG_IFLS_TX4_8, pup, REG_IFLS); + + spin_lock_irq(&pup->port.lock); + + /* restore RTS and DTR */ + cr = pup->old_cr & (REG_CR_RTS | REG_CR_DTR); + cr |= REG_CR_UARTEN | REG_CR_RXE | REG_CR_TXE; + phytium_uart_write(cr, pup, REG_CR); + + spin_unlock_irq(&pup->port.lock); + + /* initialise the old status of the modem signals */ + pup->old_status = phytium_uart_read(pup, REG_FR) & (REG_FR_DCD|REG_FR_DSR|REG_FR_CTS); + + phytium_enable_interrtups(pup); + +out: + return ret; +} + +static void phytium_shutdown_channel(struct phytium_uart_port *pup, + unsigned int lcrh) +{ + unsigned long val; + + val = phytium_uart_read(pup, lcrh); + val &= ~(REG_LCRH_BRK | REG_LCRH_FEN); + phytium_uart_write(val, pup, lcrh); +} + +static void phytium_disable_uart(struct phytium_uart_port *pup) +{ + unsigned int cr; + + pup->port.status &= ~(UPSTAT_AUTOCTS | UPSTAT_AUTORTS); + spin_lock_irq(&pup->port.lock); + cr = phytium_uart_read(pup, REG_CR); + pup->old_cr = cr; + cr &= REG_CR_RTS | REG_CR_DTR; + cr |= REG_CR_UARTEN | REG_CR_TXE; + phytium_uart_write(cr, pup, REG_CR); + spin_unlock_irq(&pup->port.lock); + + /* + * disable break condition and fifos + */ + phytium_shutdown_channel(pup, REG_LCRH_RX); +} + +static void phytium_disable_interrupts(struct phytium_uart_port *pup) +{ + spin_lock_irq(&pup->port.lock); + + /* mask all interrupts and clear all pending ones */ + pup->im = 0; + phytium_uart_write(pup->im, pup, REG_IMSC); + phytium_uart_write(0xffff, pup, REG_ICR); + + spin_unlock_irq(&pup->port.lock); +} + +static void phytium_shutdown(struct uart_port *port) +{ + struct phytium_uart_port *pup = + container_of(port, struct phytium_uart_port, port); + + phytium_disable_interrupts(pup); + + free_irq(pup->port.irq, pup); + + phytium_disable_uart(pup); + + if (pup->port.ops->flush_buffer) + pup->port.ops->flush_buffer(port); +} + +static void +phytium_setup_status_masks(struct uart_port *port, struct ktermios *termios) +{ + port->read_status_mask = REG_DR_OE | 255; + if (termios->c_iflag & INPCK) + port->read_status_mask |= REG_DR_FE | REG_DR_PE; + if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK)) + port->read_status_mask |= REG_DR_BE; + + /* + * Characters to ignore + */ + port->ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= REG_DR_FE | REG_DR_PE; + if (termios->c_iflag & IGNBRK) { + port->ignore_status_mask |= REG_DR_BE; + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= REG_DR_OE; + } + + /* + * Ignore all characters if CREAD is not set. + */ + if ((termios->c_cflag & CREAD) == 0) + port->ignore_status_mask |= UART_DUMMY_DR_RX; +} + +static void +phytium_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) +{ + struct phytium_uart_port *pup = + container_of(port, struct phytium_uart_port, port); + unsigned int lcr_h, old_cr; + unsigned long flags; + unsigned int baud, quot; + + /* Ask the core to calculate the divisor for us. */ + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); + + if (baud > port->uartclk/16) + quot = DIV_ROUND_CLOSEST(port->uartclk * 8, baud); + else + quot = DIV_ROUND_CLOSEST(port->uartclk * 4, baud); + + switch (termios->c_cflag & CSIZE) { + case CS5: + lcr_h = REG_LCRH_WLEN_5; + break; + case CS6: + lcr_h = REG_LCRH_WLEN_6; + break; + case CS7: + lcr_h = REG_LCRH_WLEN_7; + break; + default: /* CS8 */ + lcr_h = REG_LCRH_WLEN_8; + break; + } + if (termios->c_cflag & CSTOPB) + lcr_h |= REG_LCRH_STP2; + if (termios->c_cflag & PARENB) { + lcr_h |= REG_LCRH_PEN; + if (!(termios->c_cflag & PARODD)) + lcr_h |= REG_LCRH_EPS; + if (termios->c_cflag & CMSPAR) + lcr_h |= REG_LCRH_SPS; + } + if (pup->port.fifosize > 1) + lcr_h |= REG_LCRH_FEN; + + spin_lock_irqsave(&port->lock, flags); + + /* + * Update the per-port timeout. + */ + uart_update_timeout(port, termios->c_cflag, baud); + + phytium_setup_status_masks(port, termios); + + if (UART_ENABLE_MS(port, termios->c_cflag)) + phytium_enable_ms(port); + + /* first, disable everything */ + old_cr = phytium_uart_read(pup, REG_CR); + phytium_uart_write(0, pup, REG_CR); + + if (termios->c_cflag & CRTSCTS) { + if (old_cr & REG_CR_RTS) + old_cr |= REG_CR_RTSEN; + + old_cr |= REG_CR_CTSEN; + port->status |= UPSTAT_AUTOCTS | UPSTAT_AUTORTS; + } else { + old_cr &= ~(REG_CR_CTSEN | REG_CR_RTSEN); + port->status &= ~(UPSTAT_AUTOCTS | UPSTAT_AUTORTS); + } + + /* Set baud rate */ + phytium_uart_write(quot & 0x3f, pup, REG_FBRD); + phytium_uart_write(quot >> 6, pup, REG_IBRD); + + phytium_uart_write(lcr_h, pup, REG_LCRH_RX); + phytium_uart_write(old_cr, pup, REG_CR); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static const char *phytium_type(struct uart_port *port) +{ + struct phytium_uart_port *pup = + container_of(port, struct phytium_uart_port, port); + return pup->port.type == PORT_PHYTIUM ? pup->type : NULL; +} + +static void phytium_release_port(struct uart_port *port) +{ + /* Nothing to release ... */ +} + +static int phytium_request_port(struct uart_port *port) +{ + /* UARTs always present */ + return 0; +} + +static void phytium_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE) { + port->type = PORT_PHYTIUM; + phytium_request_port(port); + } +} + +static int phytium_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + int ret = 0; + + if (ser->type != PORT_UNKNOWN && ser->type != PORT_PHYTIUM) + ret = -EINVAL; + if (ser->irq < 0 || ser->irq >= nr_irqs) + ret = -EINVAL; + if (ser->baud_base < 9600) + ret = -EINVAL; + + return ret; +} + +static const struct uart_ops phytium_uart_ops = { + .tx_empty = phytium_tx_empty, + .set_mctrl = phytium_set_mctrl, + .get_mctrl = phytium_get_mctrl, + .stop_tx = phytium_stop_tx, + .start_tx = phytium_start_tx, + .stop_rx = phytium_stop_rx, + .enable_ms = phytium_enable_ms, + .break_ctl = phytium_break_ctl, + .startup = phytium_startup, + .shutdown = phytium_shutdown, + .set_termios = phytium_set_termios, + .type = phytium_type, + .release_port = phytium_release_port, + .request_port = phytium_request_port, + .config_port = phytium_config_port, + .verify_port = phytium_verify_port, +}; + +static struct phytium_uart_port *uart_ports[UART_NR]; + +static struct uart_driver phytium_uart = { + .owner = THIS_MODULE, + .driver_name = DRV_NAME, + .dev_name = "ttyFTX", + .nr = UART_NR, +}; + +void phytium_unregister_port(struct phytium_uart_port *pup) +{ + int i; + bool busy = false; + + for (i = 0; i < ARRAY_SIZE(uart_ports); i++) { + if (uart_ports[i] == pup) + uart_ports[i] = NULL; + else if (uart_ports[i]) + busy = true; + } + + if (!busy) + uart_unregister_driver(&phytium_uart); +} + +static int phytium_find_free_port(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(uart_ports); i++) + if (uart_ports[i] == NULL) + return i; + + return -EBUSY; +} + +static int phytium_register_port(struct phytium_uart_port *pup) +{ + int rc; + + /* Ensure interrupts from this UART are masked and cleared */ + phytium_uart_write(0, pup, REG_IMSC); + phytium_uart_write(0xffff, pup, REG_ICR); + + if (!phytium_uart.state) { + rc = uart_register_driver(&phytium_uart); + if (rc < 0) { + dev_err(pup->port.dev, + "Failed to register Phytium PCI UART driver\n"); + return rc; + } + } + + rc = uart_add_one_port(&phytium_uart, &pup->port); + if (rc) + phytium_unregister_port(pup); + + return rc; +} + +static int phytium_uart_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct phytium_uart_port *pup; + int portnr, rc; + + portnr = phytium_find_free_port(); + if (portnr < 0) + return portnr; + + pup = devm_kzalloc(&pdev->dev, sizeof(struct phytium_uart_port), + GFP_KERNEL); + if (!pup) + return -ENOMEM; + + rc = pcim_enable_device(pdev); + if (rc) + return rc; + + rc = pcim_iomap_regions_request_all(pdev, 0x01, pci_name(pdev)); + if (rc) + return rc; + + pup->port.iotype = UPIO_MEM32; + pup->port.irq = pdev->irq; + pup->port.mapbase = pci_resource_start(pdev, 0); + pup->port.membase = pcim_iomap_table(pdev)[0]; + pup->port.ops = &phytium_uart_ops; + pup->port.dev = &pdev->dev; + pup->port.fifosize = 32; + pup->port.flags = UPF_BOOT_AUTOCONF; + pup->port.line = portnr; + + uart_ports[portnr] = pup; + + pup->old_cr = 0; + snprintf(pup->type, sizeof(pup->type), "pci-uart"); + + pci_set_drvdata(pdev, pup); + + return phytium_register_port(pup); +} + +static void phytium_uart_remove(struct pci_dev *pdev) +{ + struct phytium_uart_port *pup = pci_get_drvdata(pdev); + + uart_remove_one_port(&phytium_uart, &pup->port); + phytium_unregister_port(pup); +} + +#ifdef CONFIG_PM_SLEEP +static int phytium_uart_suspend(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct phytium_uart_port *pup = pci_get_drvdata(pdev); + + if (pup) + uart_suspend_port(&phytium_uart, &pup->port); + + return 0; +} + +static int phytium_uart_resume(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct phytium_uart_port *pup = pci_get_drvdata(pdev); + + if (pup) + uart_resume_port(&phytium_uart, &pup->port); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(phytium_dev_pm_ops, phytium_uart_suspend, phytium_uart_resume); + +static const struct pci_device_id pci_ids[] = { + { PCI_VDEVICE(PHYTIUM, 0xdc2e) }, + { 0 } +}; +MODULE_DEVICE_TABLE(pci, pci_ids); + +static struct pci_driver phytium_uart_pci_driver = { + .name = DRV_NAME, + .probe = phytium_uart_probe, + .remove = phytium_uart_remove, + .driver = { + .pm = &phytium_dev_pm_ops, + }, + .id_table = pci_ids, +}; + +static int __init phytium_uart_init(void) +{ + pr_info("Serial: Phytium PCI UART driver\n"); + + return pci_register_driver(&phytium_uart_pci_driver); +} + +static void __exit phytium_uart_exit(void) +{ + pci_unregister_driver(&phytium_uart_pci_driver); +} + +module_init(phytium_uart_init); +module_exit(phytium_uart_exit); + +MODULE_AUTHOR("Chen Baozi "); +MODULE_DESCRIPTION("Phytium PCI serial port driver"); +MODULE_LICENSE("GPL"); diff --git a/target/linux/phytium/files-5.10/drivers/usb/phytium/Kconfig b/target/linux/phytium/files-5.10/drivers/usb/phytium/Kconfig new file mode 100644 index 000000000..f131a3f88 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/usb/phytium/Kconfig @@ -0,0 +1,8 @@ +config USB_PHYTIUM + tristate "PHYTIUM USB Support" + depends on USB && USB_GADGET + help + Say Y or M here if your system has a OTG USB Controller based on PHYTIUM SOC. + + If you choose to build this driver is a dynamically linked modules, the module will + be called phytium-usb.ko diff --git a/target/linux/phytium/files-5.10/drivers/usb/phytium/Makefile b/target/linux/phytium/files-5.10/drivers/usb/phytium/Makefile new file mode 100644 index 000000000..05d422d4a --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/usb/phytium/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_USB_PHYTIUM) += phytium-usb.o + +phytium-usb-y := core.o dma.o platform.o host.o gadget.o diff --git a/target/linux/phytium/files-5.10/drivers/usb/phytium/core.c b/target/linux/phytium/files-5.10/drivers/usb/phytium/core.c new file mode 100644 index 000000000..c0182c077 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/usb/phytium/core.c @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include "core.h" + +int phytium_core_reset(struct phytium_cusb *config, bool skip_wait) +{ + if (!config) + return 0; + + spin_lock_init(&config->lock); + + return 0; +} + +uint32_t phytium_read32(uint32_t *address) +{ + return readl(address); +} + +void phytium_write32(uint32_t *address, uint32_t value) +{ + writel(value, address); +} + +uint16_t phytium_read16(uint16_t *address) +{ + return readw(address); +} + +void phytium_write16(uint16_t *address, uint16_t value) +{ + writew(value, address); +} + +uint8_t phytium_read8(uint8_t *address) +{ + return readb(address); +} + +void phytium_write8(uint8_t *address, uint8_t value) +{ + writeb(value, address); +} + diff --git a/target/linux/phytium/files-5.10/drivers/usb/phytium/core.h b/target/linux/phytium/files-5.10/drivers/usb/phytium/core.h new file mode 100644 index 000000000..f563672cc --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/usb/phytium/core.h @@ -0,0 +1,98 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __PHYTIUM_CORE_H__ +#define __PHYTIUM_CORE_H__ + +#include +#include +#include "host_api.h" +#include "gadget.h" + +#define MAX_EPS_CHANNELS 16 + +struct phytium_ep { + struct phytium_cusb *config; + u16 max_packet; + u8 ep_num; + struct GADGET_EP *gadget_ep; + struct list_head req_list; + struct usb_ep end_point; + char name[12]; + u8 is_tx; + const struct usb_endpoint_descriptor *desc; + u8 busy; +}; + +struct phytium_request { + struct usb_request request; + struct GADGET_REQ *gadget_request; + struct list_head list; + struct phytium_ep *ep; + struct phytium_cusb *config; + u8 is_tx; + u8 epnum; +}; + +struct phytium_cusb { + struct device *dev; + void __iomem *regs; + void __iomem *phy_regs; + int irq; + spinlock_t lock; + enum usb_dr_mode dr_mode; + + struct GADGET_OBJ *gadget_obj; + struct GADGET_CFG gadget_cfg; + struct GADGET_CALLBACKS gadget_callbacks; + struct GADGET_SYSREQ gadget_sysreq; + struct GADGET_DEV *gadget_dev; + void *gadget_priv; + + struct usb_gadget gadget; + struct usb_gadget_driver *gadget_driver; + struct phytium_ep endpoints_tx[MAX_EPS_CHANNELS]; + struct phytium_ep endpoints_rx[MAX_EPS_CHANNELS]; + u8 ep0_data_stage_is_tx; + + struct HOST_OBJ *host_obj; + struct HOST_CFG host_cfg; + struct HOST_CALLBACKS host_callbacks; + struct HOST_SYSREQ host_sysreq; + void *host_priv; + struct usb_hcd *hcd; + + struct DMA_OBJ *dma_obj; + struct DMA_CFG dma_cfg; + struct DMA_CALLBACKS dma_callbacks; + struct DMA_SYSREQ dma_sysreq; + bool isVhubHost; +}; + +int phytium_core_reset(struct phytium_cusb *config, bool skip_wait); + +int phytium_host_init(struct phytium_cusb *config); +int phytium_host_uninit(struct phytium_cusb *config); + +#ifdef CONFIG_PM +int phytium_host_resume(void *priv); +int phytium_host_suspend(void *priv); +int phytium_gadget_resume(void *priv); +int phytium_gadget_suspend(void *priv); +#endif + +int phytium_gadget_init(struct phytium_cusb *config); +int phytium_gadget_uninit(struct phytium_cusb *config); + +uint32_t phytium_read32(uint32_t *address); + +void phytium_write32(uint32_t *address, uint32_t value); + +uint16_t phytium_read16(uint16_t *address); + +void phytium_write16(uint16_t *address, uint16_t value); + +uint8_t phytium_read8(uint8_t *address); + +void phytium_write8(uint8_t *address, uint8_t value); + +#endif diff --git a/target/linux/phytium/files-5.10/drivers/usb/phytium/dma.c b/target/linux/phytium/files-5.10/drivers/usb/phytium/dma.c new file mode 100644 index 000000000..2e0d6df3e --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/usb/phytium/dma.c @@ -0,0 +1,786 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include "core.h" +#include "dma.h" +#include "hw-regs.h" + +#define TRB_POOL_SIZE (sizeof(struct DMA_Trb) * (NUM_OF_TRB)) +/* burst length 'x' should be multiples of 2 */ +#define TRB_BURST_LENGTH(x) (((x) & 0xFF) << 24) + +#define BUILD_NORMAL_TRB_NO_IOC(trb, data_ptr, data_size, stream_id) { \ + trb.dmaAddr = data_ptr; \ + trb.dmaSize = data_size; \ + trb.ctrl = (stream_id << 16) | TD_TYPE_NORMAL | TDF_CYCLE_BIT; } + +#define BUILD_NORMAL_TRB_NO_IOC_CHAIN(trb, data_ptr, data_size, stream_id) { \ + trb.dmaAddr = data_ptr; \ + trb.dmaSize = data_size; \ + trb.ctrl = (stream_id << 16) | TD_TYPE_NORMAL | TDF_CYCLE_BIT | TDF_CHAIN_BIT; } + +#define BUILD_NORMAL_TRB(trb, data_ptr, data_size, stream_id) { \ + trb.dmaAddr = data_ptr; \ + trb.dmaSize = (data_size) | TRB_BURST_LENGTH(0x80); \ + trb.ctrl = (stream_id << 16) | TD_TYPE_NORMAL | TDF_CYCLE_BIT |\ + TDF_INT_ON_COMPLECTION | TDF_INT_ON_SHORT_PACKET; } + +#define BUILD_LINK_TRB(trb, target_ptr) { \ + trb.dmaAddr = target_ptr; \ + trb.dmaSize = 0; \ + trb.ctrl = TD_TYPE_LINK | TDF_CYCLE_BIT | TDF_CHAIN_BIT; } + +#define BUILD_EMPTY_TRB(trb) { \ + trb.dmaAddr = 0; \ + trb.dmaSize = 0; \ + trb.ctrl = 0; } + + +uint32_t divRoundUp(uint32_t divident, uint32_t divisor) +{ + return divisor ? ((divident + divisor - 1) / divisor) : 0; +} + +static inline struct DMA_TrbChainDesc *GetTrbChainDescEntry(struct list_head *list) +{ + return (struct DMA_TrbChainDesc *)((uintptr_t)list - + (uintptr_t)&(((struct DMA_TrbChainDesc *)0)->chainNode)); +} + +static int32_t phytium_dma_probe(struct DMA_CFG *config, struct DMA_SYSREQ *sysReq) +{ + if (!sysReq) + return -EINVAL; + + sysReq->trbMemSize = TRB_POOL_SIZE; + sysReq->privDataSize = sizeof(struct DMA_CONTROLLER); + + return 0; +} + +static int32_t phytium_dma_init(struct DMA_CONTROLLER *priv, + const struct DMA_CFG *config, struct DMA_CALLBACKS *callbacks) +{ + if (!priv || !config || !callbacks) + return -EINVAL; + + if (!config->trbAddr || !config->trbDmaAddr) + return -EINVAL; + + memset((void *)priv, 0, sizeof(struct DMA_CONTROLLER)); + memset((void *)config->trbAddr, 0, TRB_POOL_SIZE); + + priv->trbDMAPoolAddr = config->trbDmaAddr; + priv->trbPoolAddr = config->trbAddr; + priv->regs = (struct DMARegs *)config->regBase; + priv->dmaCfg = *config; + priv->dmaDrv = DMA_GetInstance(); + priv->dmaCallbacks = *callbacks; + priv->isHostCtrlMode = 0; + return 0; +} + +static void phytium_dma_destroy(struct DMA_CONTROLLER *priv) +{ + +} + +static int32_t phytium_dma_start(struct DMA_CONTROLLER *priv) +{ + int i; + + if (!priv) + return -EINVAL; + + priv->dmaMode = DMA_MODE_CHANNEL_INDIVIDUAL; + if ((priv->dmaCfg.dmaModeRx & priv->dmaCfg.dmaModeTx) == 0xFFFF) { + priv->dmaMode = DMA_MODE_GLOBAL_DMULT; + phytium_write32(&priv->regs->conf, DMARF_DMULT); + } else if ((priv->dmaCfg.dmaModeRx | priv->dmaCfg.dmaModeTx)) { + priv->dmaMode = DMA_MODE_GLOBAL_DSING; + phytium_write32(&priv->regs->conf, DMARF_DSING); + } + + for (i = 0; i < MAX_DMA_CHANNELS; i++) { + if (priv->dmaCfg.dmaModeRx & (1 << i)) { + priv->rx[i].dmultEnabled = 1; + priv->rx[i].maxTrbLen = TD_DMULT_MAX_TRB_DATA_SIZE; + priv->rx[i].maxTdLen = TD_DMULT_MAX_TD_DATA_SIZE; + } else { + priv->rx[i].dmultEnabled = 0; + priv->rx[i].maxTrbLen = TD_SING_MAX_TRB_DATA_SIZE; + priv->rx[i].maxTdLen = TD_SING_MAX_TD_DATA_SIZE; + } + priv->rx[i].lastTransferLength = 0; + + if (priv->dmaCfg.dmaModeTx & (1 << i)) { + priv->tx[i].dmultEnabled = 1; + priv->tx[i].maxTrbLen = TD_DMULT_MAX_TRB_DATA_SIZE; + priv->tx[i].maxTdLen = TD_DMULT_MAX_TD_DATA_SIZE; + } else { + priv->tx[i].dmultEnabled = 0; + priv->tx[i].maxTrbLen = TD_SING_MAX_TRB_DATA_SIZE; + priv->tx[i].maxTdLen = TD_SING_MAX_TD_DATA_SIZE; + } + priv->tx[i].lastTransferLength = 0; + } + + return 0; +} + +static uint32_t phytium_dma_stop(struct DMA_CONTROLLER *priv) +{ + + return 0; +} + +static int32_t updateDescBuffer(struct DMA_CONTROLLER *priv, + struct DMA_TrbChainDesc *trbChainDesc, uint16_t status) +{ + uint32_t ep_sel, i; + struct DMA_Channel *channel; + + if (!priv || !trbChainDesc) + return -EINVAL; + + channel = trbChainDesc->channel; + if (!channel) + return -EINVAL; + + if (channel->isIsoc && trbChainDesc->lastTrbIsLink) { + if (channel->trbChainDescList.prev == channel->trbChainDescList.next) + return 0; + } + + for (i = 0; i < trbChainDesc->numOfTrbs; i++) { + if (trbChainDesc->framesDesc) { + if (trbChainDesc->isoError) + trbChainDesc->framesDesc[i].length = 0; + else + trbChainDesc->framesDesc[i].length = + (uint32_t)trbChainDesc->trbPool[i].dmaSize & TD_SIZE_MASK; + } + + trbChainDesc->actualLen += + (uint32_t)trbChainDesc->trbPool[i].dmaSize & TD_SIZE_MASK; + } + + if (trbChainDesc->isoError) + trbChainDesc->actualLen = 0; + + ep_sel = phytium_read32(&priv->regs->ep_sel); + channel->lastTransferLength = trbChainDesc->actualLen; + + if (!trbChainDesc->lastTrbIsLink) { + if (!list_empty(&trbChainDesc->chainNode)) { + for (i = 0; i < trbChainDesc->mapSize; i++) + priv->trbChainFreeMap[(trbChainDesc->start >> 3) + i] = 0; + + trbChainDesc->channel->numOfTrbChain--; + trbChainDesc->reserved = 0; + list_del(&trbChainDesc->chainNode); + trbChainDesc = NULL; + } + } + + if (channel->trbChainDescList.prev == &channel->trbChainDescList) + channel->status = DMA_STATUS_FREE; + + if (priv->dmaCallbacks.complete) + priv->dmaCallbacks.complete(priv->parent, channel->hwUsbEppNum, + channel->isDirTx, false); + + if (channel->trbChainDescList.next != &channel->trbChainDescList) { + trbChainDesc = GetTrbChainDescEntry(channel->trbChainDescList.next); + if (trbChainDesc && trbChainDesc->lastTrbIsLink) { + if (!list_empty(&trbChainDesc->chainNode)) { + for (i = 0; i < trbChainDesc->mapSize; i++) + priv->trbChainFreeMap[(trbChainDesc->start >> 3) + i] = 0; + + trbChainDesc->channel->numOfTrbChain--; + trbChainDesc->reserved = 0; + list_del(&trbChainDesc->chainNode); + } + } + } + + phytium_write32(&priv->regs->ep_sel, ep_sel); + + return 0; +} + +static void phytium_dma_isr(struct DMA_CONTROLLER *priv) +{ + uint32_t ep_sts, ep_ists, ep_cfg; + int i; + uint8_t isDirTx, epNum; + struct DMA_Channel *channel; + struct DMA_TrbChainDesc *trbChainDesc; + + if (!priv) + return; + + ep_ists = phytium_read32(&priv->regs->ep_ists); + if (!ep_ists) { + phytium_write32(&priv->regs->ep_sts, DMARF_EP_IOC | + DMARF_EP_ISP | DMARF_EP_TRBERR); + return; + } + + for (i = 0; i < 32; i++) { + if (!(ep_ists & (1 << i))) + continue; + + isDirTx = i > 15 ? DMARD_EP_TX : 0u; + epNum = isDirTx ? i - 16 : i; + phytium_write32(&priv->regs->ep_sel, epNum | isDirTx); + + if (isDirTx) + channel = &priv->tx[epNum]; + else + channel = &priv->rx[epNum]; + + ep_sts = phytium_read32(&priv->regs->ep_sts); + + if (ep_sts & DMARF_EP_TRBERR) + phytium_write32(&priv->regs->ep_sts, DMARF_EP_TRBERR); + + if ((ep_sts & DMARF_EP_IOC) || (ep_sts & DMARF_EP_ISP) || (channel->dmultGuard + & DMARF_EP_IOC) || (channel->dmultGuard & DMARF_EP_ISP)) { +retransmit: + phytium_write32(&priv->regs->ep_sts, DMARF_EP_IOC | DMARF_EP_ISP); + trbChainDesc = GetTrbChainDescEntry(channel->trbChainDescList.next); + if (!(ep_sts & DMARF_EP_TRBERR) && channel->dmultEnabled + && !trbChainDesc->lastTrbIsLink) { + if (!priv->isHostCtrlMode && !channel->isDirTx) { + channel->dmultGuard = 0; + phytium_write32(&priv->regs->ep_sts, DMARF_EP_TRBERR); + ep_cfg = phytium_read32(&priv->regs->ep_cfg); + ep_cfg &= ~DMARV_EP_ENABLED; + phytium_write32(&priv->regs->ep_cfg, (uint32_t)ep_cfg); + updateDescBuffer(priv, trbChainDesc, 0); + break; + } + channel->dmultGuard = ep_sts; + break; + } + + channel->dmultGuard = 0; + updateDescBuffer(priv, trbChainDesc, 0); + } + + if (ep_sts & DMARF_EP_OUTSMM) + phytium_write32(&priv->regs->ep_sts, DMARF_EP_OUTSMM); + + if (ep_sts & DMARF_EP_DESCMIS) + phytium_write32(&priv->regs->ep_sts, DMARF_EP_DESCMIS); + + if (ep_sts & DMARF_EP_ISOERR) { + phytium_write32(&priv->regs->ep_sts, DMARF_EP_ISOERR); + trbChainDesc = GetTrbChainDescEntry(channel->trbChainDescList.next); + trbChainDesc->isoError = 1; + goto retransmit; + } + } +} + +static void phytium_dma_errIsr(struct DMA_CONTROLLER *priv, uint8_t irqNr, uint8_t isDirTx) +{ + struct DMA_Channel *channel; + + if (!priv) + return; + + if (isDirTx) + channel = &priv->tx[irqNr]; + else + channel = &priv->rx[irqNr]; + + if (channel->status >= DMA_STATUS_BUSY) + channel->status = DMA_STATUS_ABORT; + + if (priv->dmaCallbacks.complete) { + if (!irqNr && priv->resubmit) { + priv->dmaCallbacks.complete(priv->parent, channel->hwUsbEppNum, + channel->isDirTx, true); + priv->resubmit = false; + } else + priv->dmaCallbacks.complete(priv->parent, channel->hwUsbEppNum, + channel->isDirTx, false); + } +} + +static void *phytium_dma_channelAlloc(struct DMA_CONTROLLER *priv, + uint8_t isDirTx, uint8_t hwEpNum, uint8_t isIsoc) +{ + struct DMA_Channel *channel; + uint32_t ep_ien, ep_cfg; + uint16_t dmaMode; + + if (!priv || (hwEpNum >= MAX_DMA_CHANNELS)) + return NULL; + + if (!priv->regs) { + pr_err("dma regs is null\n"); + return NULL; + } + + ep_ien = phytium_read32(&priv->regs->ep_ien); + + if (isDirTx) { + if (priv->tx[hwEpNum].status > DMA_STATUS_FREE) + return NULL; + + channel = &priv->tx[hwEpNum]; + channel->isDirTx = 0x80; + ep_ien |= (0x01 << (hwEpNum + 16)); + dmaMode = priv->dmaCfg.dmaModeTx; + } else { + if (priv->rx[hwEpNum].status > DMA_STATUS_FREE) + return NULL; + + channel = &priv->rx[hwEpNum]; + channel->isDirTx = 0x00; + ep_ien |= (0x01 << hwEpNum); + dmaMode = priv->dmaCfg.dmaModeRx; + } + + channel->isIsoc = 0; + if (isIsoc) + channel->isIsoc = 1; + + INIT_LIST_HEAD(&channel->trbChainDescList); + channel->numOfTrbChain = 0; + channel->controller = priv; + channel->dmultGuard = 0; + channel->status = DMA_STATUS_FREE; + channel->hwUsbEppNum = hwEpNum; + + phytium_write32(&priv->regs->ep_sel, (uint32_t)hwEpNum | channel->isDirTx); + ep_cfg = phytium_read32(&priv->regs->ep_cfg); + + if (priv->dmaMode == DMA_MODE_CHANNEL_INDIVIDUAL) { + if (dmaMode & (1 << hwEpNum)) + ep_cfg |= DMARV_EP_DMULT; + else + ep_cfg |= DMARV_EP_DSING; + } + + phytium_write32(&priv->regs->ep_sts, DMARF_EP_IOC | DMARF_EP_ISP | DMARF_EP_TRBERR); + phytium_write32(&priv->regs->ep_cfg, (uint32_t)DMARV_EP_ENABLED | ep_cfg); + phytium_write32(&priv->regs->ep_ien, ep_ien); + phytium_write32(&priv->regs->ep_sts_en, DMARF_EP_TRBERR); + + return channel; +} + +static int32_t phytium_dma_channelRelease(struct DMA_CONTROLLER *priv, struct DMA_Channel *channel) +{ + uint32_t ep_ien, i; + struct DMA_TrbChainDesc *trbChainDesc; + + if (!channel || !priv) + return -EINVAL; + + ep_ien = phytium_read32(&priv->regs->ep_ien); + if (channel->isDirTx) + ep_ien &= ~(0x01 << (channel->hwUsbEppNum + 16)); + else + ep_ien &= ~(0x01 << channel->hwUsbEppNum); + + phytium_write32(&priv->regs->ep_sel, (uint32_t)channel->hwUsbEppNum | channel->isDirTx); + phytium_write32(&priv->regs->ep_cfg, (uint32_t)0); + phytium_write32(&priv->regs->ep_ien, ep_ien); + phytium_write32(&priv->regs->ep_sts_en, 0x0); + phytium_write32(&priv->regs->ep_cmd, DMARF_EP_EPRST); + phytium_write32(&priv->regs->ep_sts, DMARF_EP_IOC | DMARF_EP_ISP | + DMARF_EP_TRBERR | DMARF_EP_ISOERR); + + if (channel->status >= DMA_STATUS_BUSY) + channel->status = DMA_STATUS_ABORT; + + priv->dmaDrv->dma_channelAbort(priv, channel); + + while (channel->trbChainDescList.next != &channel->trbChainDescList) { + trbChainDesc = GetTrbChainDescEntry(channel->trbChainDescList.next); + if (trbChainDesc) { + if (!list_empty(&trbChainDesc->chainNode)) { + for (i = 0; i < trbChainDesc->mapSize; i++) + priv->trbChainFreeMap[(trbChainDesc->start >> 3) + i] = 0; + + trbChainDesc->channel->numOfTrbChain--; + trbChainDesc->reserved = 0; + list_del(&trbChainDesc->chainNode); + } + } + } + + channel->status = DMA_STATUS_UNKNOW; + + return 0; +} + +static void ShowTrbChain(struct DMA_TrbChainDesc *trbChainDesc) +{ + int i; + + if (!trbChainDesc) + return; + pr_debug("Trb Chain %p for channel %p\n", trbChainDesc, trbChainDesc->channel); + pr_debug("idx | trb size | trb addr | FLAG: NORMAL LINK CHAIN ALL\n"); + for (i = 0; i < trbChainDesc->numOfTrbs + 2; i++) + pr_debug("%02d | %08x | %08x | %d %d %d %08x\n", + i, + trbChainDesc->trbPool[i].dmaSize & TD_SIZE_MASK, + trbChainDesc->trbPool[i].dmaAddr, + (trbChainDesc->trbPool[i].ctrl & TD_TYPE_NORMAL) ? 1 : 0, + (trbChainDesc->trbPool[i].ctrl & TD_TYPE_LINK) ? 1 : 0, + (trbChainDesc->trbPool[i].ctrl & TDF_CHAIN_BIT) ? 1 : 0, + trbChainDesc->trbPool[i].ctrl); +} + +static uint32_t phytium_dma_NewTd(struct DMA_CONTROLLER *priv, + struct DMA_TrbChainDesc *trbChainDesc) +{ + uint32_t startAddress, dataSize; + struct DMA_Channel *channel; + int i = 0; + uint32_t tmp = 0; + + if (!priv || !trbChainDesc) + return 0; + + startAddress = trbChainDesc->dwStartAddress; + channel = trbChainDesc->channel; + dataSize = channel->maxTrbLen; + + if (trbChainDesc->numOfTrbs > 1) { + for (i = 0; i < (trbChainDesc->numOfTrbs - 1); i++) { + if (channel->dmultEnabled) { + if (trbChainDesc->framesDesc && priv->isHostCtrlMode + && channel->isIsoc) { + BUILD_NORMAL_TRB_NO_IOC(trbChainDesc->trbPool[i], + trbChainDesc->dwStartAddress + + trbChainDesc->framesDesc[i].offset, + trbChainDesc->framesDesc[i].length, 0); + continue; + } else + BUILD_NORMAL_TRB_NO_IOC(trbChainDesc->trbPool[i], + startAddress, dataSize, 0); + } else + BUILD_NORMAL_TRB_NO_IOC_CHAIN(trbChainDesc->trbPool[i], + startAddress, dataSize, 0); + startAddress += dataSize; + tmp += dataSize; + } + } + + if (trbChainDesc->framesDesc && priv->isHostCtrlMode && channel->isIsoc) { + BUILD_NORMAL_TRB(trbChainDesc->trbPool[i], + trbChainDesc->dwStartAddress + trbChainDesc->framesDesc[i].offset, + trbChainDesc->framesDesc[i].length, 0); + } else + BUILD_NORMAL_TRB(trbChainDesc->trbPool[i], startAddress, + trbChainDesc->len - tmp, 0); + + trbChainDesc->lastTrbIsLink = 0; + if (channel->dmultEnabled) { + if (channel->isIsoc) { + trbChainDesc->lastTrbIsLink = 1; + BUILD_LINK_TRB(trbChainDesc->trbPool[i + 1], trbChainDesc->trbDMAAddr); + } else + BUILD_EMPTY_TRB(trbChainDesc->trbPool[i + 1]); + } + + ShowTrbChain(trbChainDesc); + + return 0; +} + +static void phytium_dma_ArmTd(struct DMA_CONTROLLER *priv, struct DMA_TrbChainDesc *trbChainDesc) +{ + uint32_t ep_sts, ep_cfg, ep_cmd; + struct DMA_TrbChainDesc *startedChain; + struct DMA_Trb *lastPrevTrb; + struct DMA_Channel *channel; + + if (!priv || !trbChainDesc) + return; + + channel = trbChainDesc->channel; + phytium_write32(&priv->regs->ep_sel, (channel->isDirTx | channel->hwUsbEppNum)); + + ep_sts = phytium_read32(&priv->regs->ep_sts); + + if (channel->status == DMA_STATUS_FREE) { + phytium_write32(&priv->regs->ep_sts, DMARF_EP_TRBERR); + phytium_write32(&priv->regs->traddr, trbChainDesc->trbDMAAddr); + phytium_write32(&priv->regs->ep_sts, DMARF_EP_TRBERR); + + ep_cfg = phytium_read32(&priv->regs->ep_cfg); + phytium_write32(&priv->regs->ep_cmd, DMARF_EP_DRDY); + if (!(ep_cfg & DMARV_EP_ENABLED)) { + ep_cfg |= DMARV_EP_ENABLED; + phytium_write32(&priv->regs->ep_cfg, ep_cfg); + } + } else { + if (channel->trbChainDescList.prev != channel->trbChainDescList.next) { + startedChain = GetTrbChainDescEntry(channel->trbChainDescList.prev->prev); + lastPrevTrb = &startedChain->trbPool[startedChain->numOfTrbs]; + startedChain->lastTrbIsLink = 1; + BUILD_LINK_TRB((*lastPrevTrb), trbChainDesc->trbDMAAddr); + ep_cmd = phytium_read32(&priv->regs->ep_cmd); + if (!(ep_cmd & DMARF_EP_DRDY)) { + phytium_write32(&priv->regs->traddr, trbChainDesc->trbDMAAddr); + phytium_write32(&priv->regs->ep_cmd, DMARF_EP_DRDY); + } + ShowTrbChain(startedChain); + } + } +} + +static int32_t phytium_dma_TrbChainAlloc(struct DMA_CONTROLLER *priv, + struct DMA_Channel *channel, uint32_t numOfTrbs, struct DMA_TrbChainDesc **chain) +{ + struct DMA_TrbChainDesc *trbChainDesc = NULL; + int i, j; + uint32_t mapcount, count = 0; + + if (!priv || !channel) + return -ENOMEM; + + for (i = 0; i < TAB_SIZE_OF_DMA_CHAIN; i++) { + if (!priv->trbChainDesc[i].reserved) { + trbChainDesc = &priv->trbChainDesc[i]; + break; + } + } + + if (!trbChainDesc) { + pr_err("No Free TRB Chain Descriptor\n"); + return -ENOMEM; + } + + INIT_LIST_HEAD(&trbChainDesc->chainNode); + *chain = NULL; + mapcount = (numOfTrbs + 2 + 7) >> 3; + + for (i = 0; i < TRB_MAP_SIZE; i++) { + if (priv->trbChainFreeMap[i] == 0x0) + count++; + else + count = 0; + + if (count == mapcount) + break; + } + + if (count != mapcount) { + pr_err("No Free TRBs count:0x%x, mapcount:0x%x\n", count, mapcount); + return -ENOMEM; + } + + trbChainDesc->reserved = 1; + trbChainDesc->channel = channel; + trbChainDesc->actualLen = 0; + trbChainDesc->mapSize = mapcount; + trbChainDesc->numOfTrbs = numOfTrbs; + trbChainDesc->start = (i + 1 - mapcount) << 3; + trbChainDesc->end = trbChainDesc->start; + trbChainDesc->trbPool = (struct DMA_Trb *)priv->trbPoolAddr + trbChainDesc->start; + trbChainDesc->trbDMAAddr = (uint32_t)(uintptr_t)((struct DMA_Trb *)priv->trbDMAPoolAddr + + trbChainDesc->start); + trbChainDesc->isoError = 0; + + list_add_tail(&trbChainDesc->chainNode, &channel->trbChainDescList); + + channel->numOfTrbChain++; + + for (j = 0; j < count; j++) + priv->trbChainFreeMap[i - j] = 0xFF; + + *chain = trbChainDesc; + + return 0; +} + +static int32_t phytium_dma_channelProgram(struct DMA_CONTROLLER *priv, + struct DMA_Channel *channel, uint16_t packetSize, uintptr_t dmaAddr, + uint32_t len, void *framesDesc, uint32_t framesNumber) +{ + uint32_t numOfTrbs; + uint8_t retval; + struct DMA_TrbChainDesc *trbChainDesc; + + if (!priv || !channel) + return -EINVAL; + + //printk(KERN_ERR "%s %d\n",__func__,__LINE__); + if (framesDesc && priv->isHostCtrlMode && channel->isIsoc) + numOfTrbs = framesNumber; + else + numOfTrbs = divRoundUp(len, channel->maxTrbLen); + + retval = phytium_dma_TrbChainAlloc(priv, channel, numOfTrbs, &trbChainDesc); + if (retval) + return retval; + + trbChainDesc->dwStartAddress = dmaAddr; + trbChainDesc->len = len; + trbChainDesc->framesDesc = framesDesc; + + channel->wMaxPacketSize = packetSize; + phytium_dma_NewTd(priv, trbChainDesc); + channel->lastTransferLength = 0; + phytium_dma_ArmTd(priv, trbChainDesc); + + channel->status = DMA_STATUS_BUSY; + + return 0; +} + +static enum DMA_Status phytium_dma_getChannelStatus(struct DMA_CONTROLLER *priv, + struct DMA_Channel *channel) +{ + uint32_t ep_cmd, ep_sts; + + if (!priv || !channel) + return DMA_STATUS_UNKNOW; + + if (channel->status >= DMA_STATUS_BUSY) { + phytium_write32(&priv->regs->ep_sel, channel->isDirTx | channel->hwUsbEppNum); + ep_cmd = phytium_read32(&priv->regs->ep_cmd); + ep_sts = phytium_read32(&priv->regs->ep_sts); + + if ((ep_cmd & DMARF_EP_DRDY) || (ep_sts & DMARF_EP_DBUSY)) + channel->status = DMA_STATUS_ARMED; + else + channel->status = DMA_STATUS_BUSY; + } + + return channel->status; +} + +static int32_t phytium_dma_channelAbort(struct DMA_CONTROLLER *priv, struct DMA_Channel *channel) +{ + struct DMA_TrbChainDesc *trbChainDesc; + uint32_t ep_cfg, i; + + if (!priv || !channel) + return -EINVAL; + + if (phytium_dma_getChannelStatus(priv, channel) >= DMA_STATUS_BUSY) + phytium_write32(&priv->regs->conf, DMARF_RESET); + + phytium_write32(&priv->regs->ep_sel, (uint32_t)(channel->isDirTx | channel->hwUsbEppNum)); + ep_cfg = phytium_read32(&priv->regs->ep_cfg); + ep_cfg &= ~DMARV_EP_ENABLED; + phytium_write32(&priv->regs->ep_cfg, ep_cfg); + phytium_write32(&priv->regs->ep_sts, 0xFFFFFFFF); + + while (channel->trbChainDescList.next != &channel->trbChainDescList) { + trbChainDesc = GetTrbChainDescEntry(channel->trbChainDescList.next); + if (trbChainDesc) { + if (!list_empty(&trbChainDesc->chainNode)) { + for (i = 0; i < trbChainDesc->mapSize; i++) + priv->trbChainFreeMap[(trbChainDesc->start >> 3) + i] = 0; + + trbChainDesc->channel->numOfTrbChain--; + trbChainDesc->reserved = 0; + list_del(&trbChainDesc->chainNode); + } + } + } + if (channel->status != DMA_STATUS_UNKNOW) + channel->status = DMA_STATUS_FREE; + + return 0; +} + +static int32_t phytium_dma_getActualLength(struct DMA_CONTROLLER *priv, struct DMA_Channel *channel) +{ + if (!priv || !channel) + return -EINVAL; + + return channel->lastTransferLength; +} + +static int32_t phytium_dma_getMaxLength(struct DMA_CONTROLLER *priv, struct DMA_Channel *channel) +{ + if (!priv || !channel) + return -EINVAL; + + return channel->maxTdLen; +} + +static int32_t phytium_dma_setMaxLength(struct DMA_CONTROLLER *priv, + struct DMA_Channel *channel, uint32_t val) +{ + if (!priv || !channel) + return -EINVAL; + + if (channel->dmultEnabled) + channel->maxTrbLen = val; + else + channel->maxTrbLen = (val > TD_SING_MAX_TRB_DATA_SIZE) ? + TD_SING_MAX_TRB_DATA_SIZE : val; + + return 0; +} + +static void phytium_dma_setParentPriv(struct DMA_CONTROLLER *priv, void *parent) +{ + if (!priv) + return; + + priv->parent = parent; +} + +void phytium_dma_controllerReset(struct DMA_CONTROLLER *priv) +{ + uint32_t conf; + + if (!priv) + return; + + conf = phytium_read32(&priv->regs->conf); + conf |= DMARF_RESET; + phytium_write32(&priv->regs->conf, conf); + + priv->resubmit = true; +} + +void phytium_dma_setHostMode(struct DMA_CONTROLLER *priv) +{ + if (!priv) + return; + + priv->isHostCtrlMode = 1; +} + +struct DMA_OBJ phytium_dma_Drv = { + .dma_probe = phytium_dma_probe, + .dma_init = phytium_dma_init, + .dma_destroy = phytium_dma_destroy, + .dma_start = phytium_dma_start, + .dma_stop = phytium_dma_stop, + .dma_isr = phytium_dma_isr, + .dma_errIsr = phytium_dma_errIsr, + .dma_channelAlloc = phytium_dma_channelAlloc, + .dma_channelRelease = phytium_dma_channelRelease, + .dma_channelProgram = phytium_dma_channelProgram, + .dma_channelAbort = phytium_dma_channelAbort, + .dma_getChannelStatus = phytium_dma_getChannelStatus, + .dma_getActualLength = phytium_dma_getActualLength, + .dma_getMaxLength = phytium_dma_getMaxLength, + .dma_setMaxLength = phytium_dma_setMaxLength, + .dma_setParentPriv = phytium_dma_setParentPriv, + .dma_controllerReset = phytium_dma_controllerReset, + .dma_setHostMode = phytium_dma_setHostMode, +}; + +struct DMA_OBJ *DMA_GetInstance(void) +{ + return &phytium_dma_Drv; +} diff --git a/target/linux/phytium/files-5.10/drivers/usb/phytium/dma.h b/target/linux/phytium/files-5.10/drivers/usb/phytium/dma.h new file mode 100644 index 000000000..073a078c1 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/usb/phytium/dma.h @@ -0,0 +1,198 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __PHYTIUM_DMA_H__ +#define __PHYTIUM_DMA_H__ + +#include +//#include "list.h" + +#define NUM_OF_TRB 1024 +#define TRB_MAP_SIZE ((NUM_OF_TRB + (sizeof(uint8_t) * 8) - 1) / (sizeof(uint8_t) * 8)) +#define MAX_DMA_CHANNELS 16 +#define TAB_SIZE_OF_DMA_CHAIN (MAX_DMA_CHANNELS * 2) + +#define DMARD_EP_TX 0x80ul +#define DMARD_EP_RX 0x00ul + +#define DMARF_EP_EPRST 0x00000001ul +#define DMARF_EP_DRDY 0x00000040ul +#define DMARF_EP_DFLUSH 0x00000080ul + +#define DMARF_EP_IOC 0x4ul +#define DMARF_EP_ISP 0x8ul +#define DMARF_EP_DESCMIS 0x10ul +#define DMARF_EP_TRBERR 0x80ul +#define DMARF_EP_DBUSY 0x200ul +#define DMARF_EP_CCS 0x800ul +#define DMARF_EP_OUTSMM 0x4000ul +#define DMARF_EP_ISOERR 0x8000ul +#define DMARF_EP_DTRANS 0x80000000ul + +#define DMARV_EP_DISABLED 0ul +#define DMARV_EP_ENABLED 1ul +#define DMARV_EP_DSING 0x1000ul +#define DMARV_EP_DMULT 0x2000ul + +#define TD_SIZE_MASK 0x00001FFFF + +#define DMARF_RESET 0x00000001ul +#define DMARF_DSING 0x00000100ul +#define DMARF_DMULT 0x00000200ul + +#define TD_DMULT_MAX_TRB_DATA_SIZE 65536u +#define TD_DMULT_MAX_TD_DATA_SIZE (~1u) +#define TD_SING_MAX_TRB_DATA_SIZE 65536u +#define TD_SING_MAX_TD_DATA_SIZE 65536u + +#define TD_TYPE_NORMAL 0x400L +#define TD_TYPE_LINK 0x1800L +#define TDF_CYCLE_BIT 0x1L +#define TDF_TOGGLE_CYCLE_BIT 0x2L +#define TDF_INT_ON_SHORT_PACKET 0x4L +#define TDF_FIFO_MODE 0x8L +#define TDF_CHAIN_BIT 0x10L +#define TDF_INT_ON_COMPLECTION 0x20L +#define TDF_STREAMID_VALID 0x200L + +struct DMA_Trb { + uint32_t dmaAddr; + uint32_t dmaSize; /* 0:16 transfer length; 24:31 burst length */ + uint32_t ctrl; +}; + +enum DMA_Status { + DMA_STATUS_UNKNOW, + DMA_STATUS_FREE, + DMA_STATUS_ABORT, + DMA_STATUS_BUSY, + DMA_STATUS_ARMED +}; + +struct DMA_CFG { + uintptr_t regBase; + uint16_t dmaModeTx; + uint16_t dmaModeRx; + void *trbAddr; + uintptr_t trbDmaAddr; +}; + +struct DMA_SYSREQ { + uint32_t privDataSize; + uint32_t trbMemSize; +}; + +struct DMA_CALLBACKS { + void (*complete)(void *pD, uint8_t epNum, uint8_t dir, bool resubmit); +}; + +struct DMA_CONTROLLER; + +struct DMA_Channel { + struct DMA_CONTROLLER *controller; + uint16_t wMaxPacketSize; + uint8_t hwUsbEppNum; + uint8_t isDirTx; + uint32_t maxTdLen; + uint32_t maxTrbLen; + enum DMA_Status status; + void *priv; + uint32_t dmultGuard; + uint8_t dmultEnabled; + uint8_t numOfTrbChain; + struct list_head trbChainDescList; + uint32_t lastTransferLength; + uint8_t isIsoc; +}; + +struct DMA_OBJ { + int32_t (*dma_probe)(struct DMA_CFG *config, struct DMA_SYSREQ *sysReq); + + int32_t (*dma_init)(struct DMA_CONTROLLER *priv, const struct DMA_CFG *config, + struct DMA_CALLBACKS *callbacks); + + void (*dma_destroy)(struct DMA_CONTROLLER *priv); + + int32_t (*dma_start)(struct DMA_CONTROLLER *priv); + + uint32_t (*dma_stop)(struct DMA_CONTROLLER *priv); + + void (*dma_isr)(struct DMA_CONTROLLER *priv); + + void (*dma_errIsr)(struct DMA_CONTROLLER *priv, uint8_t irqNr, uint8_t isDirTx); + + void * (*dma_channelAlloc)(struct DMA_CONTROLLER *priv, + uint8_t isDirTx, uint8_t hwEpNum, uint8_t isIso); + + int32_t (*dma_channelRelease)(struct DMA_CONTROLLER *priv, struct DMA_Channel *channel); + + int32_t (*dma_channelProgram)(struct DMA_CONTROLLER *priv, struct DMA_Channel *channel, + uint16_t packetSize, uintptr_t dmaAddr, + uint32_t len, void *framesDesc, uint32_t framesNumber); + + int32_t (*dma_channelAbort)(struct DMA_CONTROLLER *priv, struct DMA_Channel *channel); + + enum DMA_Status (*dma_getChannelStatus)(struct DMA_CONTROLLER *priv, + struct DMA_Channel *channel); + + int32_t (*dma_getActualLength)(struct DMA_CONTROLLER *priv, struct DMA_Channel *channel); + + int32_t (*dma_getMaxLength)(struct DMA_CONTROLLER *priv, struct DMA_Channel *channel); + + int32_t (*dma_setMaxLength)(struct DMA_CONTROLLER *priv, + struct DMA_Channel *channel, uint32_t val); + + void (*dma_setParentPriv)(struct DMA_CONTROLLER *priv, void *parent); + + void (*dma_controllerReset)(struct DMA_CONTROLLER *priv); + + void (*dma_setHostMode)(struct DMA_CONTROLLER *priv); +}; + +enum DMA_Mode { + DMA_MODE_GLOBAL_DMULT, + DMA_MODE_GLOBAL_DSING, + DMA_MODE_CHANNEL_INDIVIDUAL, +}; + +struct DMA_TrbFrameDesc { + uint32_t length; + uint32_t offset; +}; + +struct DMA_TrbChainDesc { + uint8_t reserved; + struct DMA_Channel *channel; + struct DMA_Trb *trbPool; + uint32_t trbDMAAddr; + uint32_t len; + uint32_t dwStartAddress; + uint32_t actualLen; + uint8_t isoError; + uint8_t lastTrbIsLink; + uint32_t mapSize; + uint32_t numOfTrbs; + uint32_t start; + uint32_t end; + struct DMA_TrbFrameDesc *framesDesc; + struct list_head chainNode; +}; + +struct DMA_CONTROLLER { + struct DMARegs *regs; + struct DMA_OBJ *dmaDrv; + struct DMA_CFG dmaCfg; + struct DMA_CALLBACKS dmaCallbacks; + struct DMA_Channel rx[MAX_DMA_CHANNELS]; + struct DMA_Channel tx[MAX_DMA_CHANNELS]; + enum DMA_Mode dmaMode; + uint8_t isHostCtrlMode; + void *parent; + void *trbPoolAddr; + uintptr_t trbDMAPoolAddr; + uint8_t trbChainFreeMap[TRB_MAP_SIZE]; + struct DMA_TrbChainDesc trbChainDesc[TAB_SIZE_OF_DMA_CHAIN]; + bool resubmit; +}; + +struct DMA_OBJ *DMA_GetInstance(void); +#endif diff --git a/target/linux/phytium/files-5.10/drivers/usb/phytium/gadget.c b/target/linux/phytium/files-5.10/drivers/usb/phytium/gadget.c new file mode 100644 index 000000000..afcb846c8 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/usb/phytium/gadget.c @@ -0,0 +1,2538 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include "gadget.h" +#include "dma.h" +#include "core.h" +#include "hw-regs.h" + +#define DRV_NAME "phytium_gadget" + +#define GADGET_PRIV_BUFFER_SIZE 64 +#define GADGET_USB_EP_NUMBER_MASK 0xf +#define DMA_ADDR_INVALID (~(dma_addr_t)0) + +#define GADGET_ESTALL 1 +#define GADGET_EUNHANDLED 2 +#define GADGET_EAUTOACK 3 +#define GADGET_ESHUTDOWN 4 +#define GADGET_ECONNRESET 5 +#define GADGET_EAGAIN 6 + +static inline struct GadgetEp *toGadgetEp(struct GADGET_EP *gadget_Ep) +{ + return (struct GadgetEp *)((uintptr_t)gadget_Ep - + ((uintptr_t)&((struct GadgetEp *)0)->gadgetEp)); +} + +static inline struct GadgetRequest *requestToGadgetRequest(struct GADGET_REQ *req) +{ + return (struct GadgetRequest *)((uintptr_t)req - + ((uintptr_t)&((struct GadgetRequest *)0)->request)); +} + +static inline struct GADGET_REQ *listToGadgetRequest(struct list_head *list) +{ + return (struct GADGET_REQ *)((uintptr_t)list - + ((uintptr_t)&((struct GADGET_REQ *)0)->list)); +} + +#define listBrowsingRequest(iterator, head, memeber) \ + for (iterator = listToGadgetRequest((head)->next); \ + &iterator->list != (head); \ + iterator = listToGadgetRequest(iterator->list.next)) + +static inline struct GADGET_REQ *gadgetGetNextReq(struct GadgetEp *gadgetEp) +{ + struct list_head *list = &gadgetEp->request; + + if (list_empty(list)) { + pr_debug("no request available for %s\n", gadgetEp->gadgetEp.name); + return NULL; + } + + return listToGadgetRequest(list->next); +} + +static inline struct GADGET_REQ *gadgetGetNextEp0Req(struct GADGET_CTRL *priv) +{ + struct list_head *queue; + + if (!priv) + return NULL; + + queue = &priv->in[0].request; + + if (list_empty(queue)) + return NULL; + + return listToGadgetRequest(queue->next); +} + +void gadget_giveback(struct phytium_ep *phy_ep, struct usb_request *usb_req, int status) +{ + struct phytium_request *phy_req; + struct phytium_cusb *config; + int busy; + + if (!phy_ep || !usb_req) + return; + + busy = phy_ep->busy; + phy_req = usb_req ? container_of(usb_req, struct phytium_request, request) : NULL; + config = phy_req->config; + + list_del(&phy_req->list); + + if (usb_req->status == -EINPROGRESS) { + if (status == GADGET_ESHUTDOWN) + usb_req->status = -ESHUTDOWN; + else if (status == GADGET_ECONNRESET) + usb_req->status = -ECONNRESET; + else + usb_req->status = -phy_req->gadget_request->status; + } + + if (usb_req->status == 0) + pr_debug("%s done request %p, %d/%d\n", phy_ep->end_point.name, + usb_req, usb_req->actual, usb_req->length); + else + pr_debug("%s request %p, %d/%d fault %d\n", phy_ep->end_point.name, + usb_req, usb_req->actual, usb_req->length, usb_req->status); + + usb_gadget_unmap_request(&config->gadget, &phy_req->request, phy_req->is_tx); + + busy = phy_ep->busy; + phy_ep->busy = 1; + + spin_unlock(&config->lock); + + if (phy_req->request.complete) + phy_req->request.complete(&phy_req->ep->end_point, usb_req); + + spin_lock(&config->lock); + + phy_ep->busy = busy; +} + +static void gadget_callback_complete(struct GADGET_EP *gadget_ep, struct GADGET_REQ *gadget_req) +{ + struct phytium_ep *phy_ep; + struct phytium_request *phy_req; + struct usb_request *usb_req; + + if (!gadget_ep || !gadget_req) + return; + + phy_req = gadget_req->context; + usb_req = &phy_req->request; + phy_ep = phy_req->ep; + usb_req->actual = gadget_req->actual; + usb_req->length = gadget_req->length; + + gadget_giveback(phy_ep, usb_req, gadget_req->status); +} + +static void gadgetDisconnect(struct GADGET_CTRL *priv) +{ + pr_info("Disconnect USB Device Driver\n"); + + if (!priv) + return; + + priv->gadgetDev.speed = USB_SPEED_UNKNOWN; + priv->gadgetDev.state = USB_STATE_NOTATTACHED; + + if (priv->eventCallback.disconnect) + priv->eventCallback.disconnect(priv); +} + +static int gadget_get_frame(struct usb_gadget *gadget) +{ + pr_info("%s %d\n", __func__, __LINE__); + + return 0; +} + +static int gadget_wakeup(struct usb_gadget *gadget) +{ + pr_info("%s %d\n", __func__, __LINE__); + + return 0; +} + +static int gadget_vbus_session(struct usb_gadget *gadget, int is_active) +{ + pr_info("%s %d\n", __func__, __LINE__); + + return 0; +} + +static int gadget_vbus_draw(struct usb_gadget *gadget, unsigned int mA) +{ + pr_info("%s %d\n", __func__, __LINE__); + + return 0; +} + +static int gadget_pullup(struct usb_gadget *gadget, int is_on) +{ + pr_info("%s %d\n", __func__, __LINE__); + + return 0; +} + +static int gadget_udc_start(struct usb_gadget *gadget, struct usb_gadget_driver *driver) +{ + unsigned long flags; + struct phytium_cusb *config; + struct GADGET_CTRL *priv; + uint32_t gen_cfg; + + if (!gadget || !driver) + return -EINVAL; + + if (driver->max_speed < USB_SPEED_HIGH) + return -EINVAL; + + config = container_of(gadget, struct phytium_cusb, gadget); + + pr_info("registering driver %s\n", driver->function); + + spin_lock_irqsave(&config->lock, flags); + config->gadget_driver = driver; + spin_unlock_irqrestore(&config->lock, flags); + + config->gadget_obj->gadget_start(config->gadget_priv); + + priv = (struct GADGET_CTRL *)config->gadget_priv; + if (priv->phy_regs) { + gen_cfg = phytium_read32(&priv->phy_regs->gen_cfg); + gen_cfg = gen_cfg & (~BIT(7)); + phytium_write32(&priv->phy_regs->gen_cfg, gen_cfg); + } + + return 0; +} + +static int gadget_udc_stop(struct usb_gadget *gadget) +{ + struct phytium_cusb *config; + unsigned long flags; + struct GADGET_CTRL *priv; + uint32_t gen_cfg; + + if (!gadget) + return -EINVAL; + + config = container_of(gadget, struct phytium_cusb, gadget); + + priv = (struct GADGET_CTRL *)config->gadget_priv; + if (priv->phy_regs) { + gen_cfg = phytium_read32(&priv->phy_regs->gen_cfg); + gen_cfg = gen_cfg | BIT(7); + phytium_write32(&priv->phy_regs->gen_cfg, gen_cfg); + } + spin_lock_irqsave(&config->lock, flags); + config->gadget_driver = NULL; + spin_unlock_irqrestore(&config->lock, flags); + + return 0; +} + +static struct usb_gadget_ops phytium_gadget_ops = { + .get_frame = gadget_get_frame, + .wakeup = gadget_wakeup, + .vbus_session = gadget_vbus_session, + .vbus_draw = gadget_vbus_draw, + .pullup = gadget_pullup, + .udc_start = gadget_udc_start, + .udc_stop = gadget_udc_stop, +}; + +static int gadget_ep_enable(struct usb_ep *ls_ep, const struct usb_endpoint_descriptor *desc) +{ + struct phytium_ep *phy_ep = NULL; + struct phytium_cusb *config; + unsigned long flags; + + if (!ls_ep || !desc) + return -EINVAL; + + phy_ep = ls_ep ? container_of(ls_ep, struct phytium_ep, end_point) : NULL; + config = phy_ep->config; + + if (phy_ep->desc) + return -EBUSY; + + spin_lock_irqsave(&config->lock, flags); + + phy_ep->desc = desc; + phy_ep->busy = 0; + config->gadget_obj->gadget_epEnable(config->gadget_priv, phy_ep->gadget_ep, desc); + + spin_unlock_irqrestore(&config->lock, flags); + + return 0; +} + +static int gadget_ep_disable(struct usb_ep *ls_ep) +{ + struct phytium_ep *phy_ep = NULL; + struct phytium_cusb *config; + unsigned long flags; + + if (!ls_ep) + return -EBUSY; + + pr_info("%s %d\n", __func__, __LINE__); + + phy_ep = ls_ep ? container_of(ls_ep, struct phytium_ep, end_point) : NULL; + config = phy_ep->config; + + spin_lock_irqsave(&config->lock, flags); + + phy_ep->desc = NULL; + phy_ep->busy = 0; + phy_ep->end_point.desc = NULL; + config->gadget_obj->gadget_epDisable(config->gadget_priv, phy_ep->gadget_ep); + + spin_unlock_irqrestore(&config->lock, flags); + + return 0; +} + +static struct usb_request *gadget_ep_alloc_request(struct usb_ep *ls_ep, gfp_t gfp_flags) +{ + struct phytium_ep *phy_ep; + struct phytium_cusb *config; + struct GADGET_EP *gadget_ep; + struct phytium_request *phy_request; + + if (!ls_ep) + return NULL; + + pr_info("%s %d\n", __func__, __LINE__); + phy_request = kzalloc(sizeof(*phy_request), gfp_flags); + if (!phy_request) { + pr_err("not enough momory\n"); + return NULL; + } + + phy_ep = ls_ep ? container_of(ls_ep, struct phytium_ep, end_point) : NULL; + config = phy_ep->config; + gadget_ep = phy_ep->gadget_ep; + + INIT_LIST_HEAD(&phy_request->list); + phy_request->request.dma = DMA_ADDR_INVALID; + phy_request->epnum = phy_ep->ep_num; + phy_request->ep = phy_ep; + phy_request->config = phy_ep->config; + + config->gadget_obj->gadget_reqAlloc(config->gadget_priv, gadget_ep, + &phy_request->gadget_request); + + return &phy_request->request; +} + +static void gadget_ep_free_request(struct usb_ep *ls_ep, struct usb_request *ls_req) +{ + struct phytium_ep *phy_ep; + struct phytium_cusb *config; + struct phytium_request *phy_request; + + if (!ls_ep || !ls_req) + return; + + pr_info("%s %d\n", __func__, __LINE__); + phy_request = ls_req ? container_of(ls_req, struct phytium_request, request) : NULL; + config = phy_request->config; + phy_ep = ls_ep ? container_of(ls_ep, struct phytium_ep, end_point) : NULL; + + config->gadget_obj->gadget_reqFree(config->gadget_priv, phy_ep->gadget_ep, + phy_request->gadget_request); + kfree(phy_request); +} + +static int gadget_ep_enqueue(struct usb_ep *ls_ep, struct usb_request *ls_req, gfp_t gfp_flags) +{ + struct phytium_ep *phy_ep; + struct phytium_cusb *config; + struct phytium_request *phy_request; + unsigned long flags; + int status = 0; + + if (!ls_ep || !ls_req) + return -EINVAL; + + if (!ls_req->buf) + return -ENODATA; + + phy_ep = ls_ep ? container_of(ls_ep, struct phytium_ep, end_point) : NULL; + config = phy_ep->config; + phy_request = ls_req ? container_of(ls_req, struct phytium_request, request) : NULL; + phy_request->config = config; + + if (phy_request->ep != phy_ep) + return -EINVAL; + + phy_request->request.actual = 0; + phy_request->request.status = -EINPROGRESS; + phy_request->epnum = phy_ep->ep_num; + phy_request->is_tx = phy_ep->is_tx; + + phy_request->gadget_request->length = ls_req->length; + phy_request->gadget_request->status = 0; + phy_request->gadget_request->complete = gadget_callback_complete; + phy_request->gadget_request->buf = ls_req->buf; + phy_request->gadget_request->context = ls_req; + + status = usb_gadget_map_request(&config->gadget, &phy_request->request, phy_request->is_tx); + + if (!phy_ep->desc) { + pr_debug("req %p queued to %s while ep %s\n", phy_request, ls_ep->name, "disabled"); + status = -ESHUTDOWN; + usb_gadget_unmap_request(&config->gadget, &phy_request->request, + phy_request->is_tx); + return status; + } + + spin_lock_irqsave(&config->lock, flags); + + phy_request->gadget_request->dma = phy_request->request.dma; + list_add_tail(&phy_request->list, &phy_ep->req_list); + + pr_debug("queue to %s (%s), length = %d\n", phy_ep->name, + phy_ep->is_tx ? "IN/TX" : "OUT/RX", phy_request->request.length); + + status = config->gadget_obj->gadget_reqQueue(config->gadget_priv, phy_ep->gadget_ep, + phy_request->gadget_request); + + spin_unlock_irqrestore(&config->lock, flags); + + return status; +} + +static int gadget_ep_dequeue(struct usb_ep *ls_ep, struct usb_request *ls_req) +{ + struct phytium_ep *phy_ep; + struct phytium_cusb *config; + unsigned long flags; + int status = 0; + struct phytium_request *phy_request; + struct phytium_request *phy_next_request; + + if (!ls_ep || !ls_req) + return -EINVAL; + + phy_ep = ls_ep ? container_of(ls_ep, struct phytium_ep, end_point) : NULL; + config = phy_ep->config; + phy_request = ls_req ? container_of(ls_req, struct phytium_request, request) : NULL; + + if (phy_request->ep != phy_ep) + return -EINVAL; + + spin_lock_irqsave(&config->lock, flags); + + list_for_each_entry(phy_next_request, &phy_ep->req_list, list) { + if (phy_next_request == phy_request) + break; + } + + if (phy_next_request != phy_request) { + pr_info("request %p not queued to %s\n", phy_request, ls_ep->name); + status = -EINVAL; + goto done; + } + + status = config->gadget_obj->gadget_reqDequeue(config->gadget_priv, phy_ep->gadget_ep, + phy_request->gadget_request); +done: + spin_unlock_irqrestore(&config->lock, flags); + return status; +} + +static int gadget_ep_set_halt(struct usb_ep *ls_ep, int value) +{ + struct phytium_ep *phy_ep; + struct phytium_cusb *config; + struct GADGET_EP *gadget_ep = NULL; + unsigned long flags; + int status = 0; + + if (!ls_ep) + return -EINVAL; + + phy_ep = ls_ep ? container_of(ls_ep, struct phytium_ep, end_point) : NULL; + config = phy_ep->config; + gadget_ep = phy_ep->gadget_ep; + + spin_lock_irqsave(&config->lock, flags); + + status = config->gadget_obj->gadget_epSetHalt(config->gadget_priv, + phy_ep->gadget_ep, value); + if (status > 0) { + spin_unlock_irqrestore(&config->lock, flags); + return -status; + } + + spin_unlock_irqrestore(&config->lock, flags); + + return 0; +} + +static const struct usb_ep_ops gadget_ep_ops = { + .enable = gadget_ep_enable, + .disable = gadget_ep_disable, + .alloc_request = gadget_ep_alloc_request, + .free_request = gadget_ep_free_request, + .queue = gadget_ep_enqueue, + .dequeue = gadget_ep_dequeue, + .set_halt = gadget_ep_set_halt, +}; + +static int gadget_ep0_enable(struct usb_ep *ep, const struct usb_endpoint_descriptor *desc) +{ + return -EINVAL; +} + +static int gadget_ep0_disable(struct usb_ep *ep) +{ + return -EINVAL; +} + +static int gadget_ep0_enqueue(struct usb_ep *ls_ep, struct usb_request *ls_req, gfp_t gfp_flags) +{ + struct phytium_ep *phy_ep; + struct phytium_request *phy_request; + struct phytium_cusb *config; + int status = 0; + unsigned long flags; + + if (!ls_ep || !ls_req) + return -EINVAL; + + phy_ep = ls_ep ? container_of(ls_ep, struct phytium_ep, end_point) : NULL; + config = phy_ep->config; + phy_request = ls_req ? container_of(ls_req, struct phytium_request, request) : NULL; + + spin_lock_irqsave(&config->lock, flags); + + if (!list_empty(&phy_ep->req_list)) { + status = -EBUSY; + goto cleanup; + } + + phy_request->config = config; + phy_request->request.actual = 0; + phy_request->gadget_request->actual = 0; + phy_request->is_tx = config->ep0_data_stage_is_tx; + phy_request->gadget_request->length = phy_request->request.length; + phy_request->gadget_request->status = 0; + phy_request->gadget_request->complete = gadget_callback_complete; + phy_request->gadget_request->buf = phy_request->request.buf; + phy_request->gadget_request->context = phy_request; + + status = usb_gadget_map_request(&config->gadget, &phy_request->request, phy_request->is_tx); + if (status) { + pr_info("failed to map request\n"); + status = -EINVAL; + goto cleanup; + } + + phy_request->gadget_request->dma = phy_request->request.dma; + list_add_tail(&phy_request->list, &phy_ep->req_list); + + pr_debug("queue to %s (%s), length = %d\n", phy_ep->name, + phy_ep->is_tx ? "IN/TX" : "OUT/RX", phy_request->request.length); + + status = config->gadget_obj->gadget_reqQueue(config->gadget_priv, phy_ep->gadget_ep, + phy_request->gadget_request); + if (status > 0) { + status = -status; + usb_gadget_unmap_request(&config->gadget, &phy_request->request, + phy_request->is_tx); + list_del(&phy_request->list); + goto cleanup; + } + +cleanup: + spin_unlock_irqrestore(&config->lock, flags); + return status; +} + +static int gadget_ep0_dequeue(struct usb_ep *ep, struct usb_request *ls_request) +{ + return -EOPNOTSUPP; +} + +static int gadget_ep0_set_halt(struct usb_ep *ep, int value) +{ + return -EINVAL; +} + + +static const struct usb_ep_ops gadget_ep0_ops = { + .enable = gadget_ep0_enable, + .disable = gadget_ep0_disable, + .alloc_request = gadget_ep_alloc_request, + .free_request = gadget_ep_free_request, + .queue = gadget_ep0_enqueue, + .dequeue = gadget_ep0_dequeue, + .set_halt = gadget_ep0_set_halt, +}; + +static int32_t gadgetWaitForBusyBit(struct GADGET_CTRL *priv, struct GADGET_EP *gadget_Ep) +{ + struct GadgetEp *gadgetEp; + uint8_t epNum; + uint8_t txcs = CS_BUSY; + uint8_t buf = 0; + uint8_t bufflag = 0; + + if (!priv || !gadget_Ep) + return -EINVAL; + + gadgetEp = toGadgetEp(gadget_Ep); + epNum = gadgetEp->hwEpNum; + + if (gadgetEp->isInEp || gadgetEp->hwEpNum == 0) + return 0; + + buf = phytium_read8(&priv->regs->ep[epNum - 1].txcon) & CON_BUF; + + while ((txcs & CS_BUSY) || (bufflag == 0)) { + txcs = phytium_read8(&priv->regs->ep[epNum - 1].txcs); + + if (((txcs & CS_NPAK) >> CS_NPAK_OFFSET) == buf || buf == 0) + bufflag = 1; + else + bufflag = 0; + } + + return 0; +} + +static inline void gadgetEpXDataReceive(struct GADGET_CTRL *priv, + struct GadgetRequest *gadgetRequest) +{ + struct GadgetEp *gadgetEp; + struct GADGET_REQ *gadgetReq; + uint8_t epType; + uint32_t requestSize, channelStatus, chMaxLen; + + if (!priv || !gadgetRequest) + return; + + gadgetEp = gadgetRequest->ep; + chMaxLen = priv->dmaDrv->dma_getMaxLength(priv->dmaController, gadgetEp->channel); + epType = gadgetEp->gadgetEp.desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + channelStatus = priv->dmaDrv->dma_getChannelStatus(priv->dmaController, gadgetEp->channel); + + gadgetReq = &gadgetRequest->request; + if (gadgetReq->actual < gadgetReq->length || gadgetRequest->zlp) { + gadgetRequest->zlp = 0; + if ((gadgetReq->length - gadgetReq->actual) < chMaxLen) + requestSize = gadgetReq->length - gadgetReq->actual; + else + requestSize = chMaxLen; + + priv->dmaDrv->dma_channelProgram(priv->dmaController, gadgetEp->channel, + gadgetEp->gadgetEp.maxPacket, + gadgetReq->dma + gadgetReq->actual, requestSize, NULL, 0); + } +} + +static inline void gadgetEpXDataSend(struct GADGET_CTRL *priv, struct GadgetRequest *gadgetRequest) +{ + struct GadgetEp *gadgetEp; + struct GADGET_REQ *gadgetReq; + uint8_t epType; + uint32_t requestSize, channelStatus, chMaxLen; + + if (!priv || !gadgetRequest) + return; + + gadgetEp = gadgetRequest->ep; + chMaxLen = priv->dmaDrv->dma_getMaxLength(priv->dmaController, gadgetEp->channel); + epType = gadgetEp->gadgetEp.desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + channelStatus = priv->dmaDrv->dma_getChannelStatus(priv->dmaController, gadgetEp->channel); + + gadgetReq = &gadgetRequest->request; + if ((gadgetReq->length - gadgetReq->actual) < chMaxLen) + requestSize = gadgetReq->length - gadgetReq->actual; + else + requestSize = chMaxLen; + pr_debug("Transmit/IN %s gadgetReq %p gadgetRequest:%p requestSize:0x%x packetSize:0x%x\n", + gadgetEp->gadgetEp.name, gadgetReq, gadgetRequest, + requestSize, gadgetEp->gadgetEp.maxPacket); + + gadgetRequest->zlp = 0; + priv->dmaDrv->dma_channelProgram(priv->dmaController, gadgetEp->channel, + gadgetEp->gadgetEp.maxPacket, + gadgetReq->dma + gadgetReq->actual, requestSize, NULL, 0); +} + +static int32_t gadgetEpXSetHalt(struct GADGET_CTRL *priv, struct GADGET_EP *gadget_Ep, + uint8_t value) +{ + struct GadgetEp *gadgetEp; + uint8_t epType; + struct GADGET_REQ *req = NULL; + struct GadgetRequest *gadgetRequest = NULL; + uint8_t epNum, txcon, rxcon; + uint32_t status = DMA_STATUS_ARMED; + + if (!priv || !gadget_Ep) + return -EINVAL; + + gadgetEp = toGadgetEp(gadget_Ep); + epType = gadgetEp->gadgetEp.desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + if (epType == USB_ENDPOINT_XFER_ISOC) + return -EINVAL; + + pr_debug("%s: %s stall\n", gadget_Ep->name, value ? "set" : "clear"); + req = gadgetGetNextReq(gadgetEp); + + if (!value) + gadgetEp->wedged = 0; + + if (value && gadgetEp->isInEp && req && gadgetEp->state == GADGET_EP_BUSY) { + while (status == DMA_STATUS_ARMED) + status = priv->dmaDrv->dma_getChannelStatus(priv->dmaController, + gadgetEp->channel); + + gadgetWaitForBusyBit(priv, gadget_Ep); + } + + epNum = gadgetEp->hwEpNum; + + if (gadgetEp->isInEp) { + txcon = phytium_read8(&priv->regs->ep[epNum - 1].txcon); + if (value) { + phytium_write8(&priv->regs->ep[epNum - 1].txcon, txcon | CON_STALL); + phytium_write8(&priv->regs->endprst, ENDPRST_IO_TX | epNum); + phytium_write8(&priv->regs->endprst, + ENDPRST_IO_TX | epNum | ENDPRST_FIFORST); + + } else { + phytium_write8(&priv->regs->endprst, ENDPRST_IO_TX | epNum); + phytium_write8(&priv->regs->endprst, + ENDPRST_IO_TX | epNum | ENDPRST_TOGRST); + phytium_write8(&priv->regs->ep[epNum - 1].txcon, txcon & (~CON_STALL)); + } + } else { + rxcon = phytium_read8(&priv->regs->ep[epNum - 1].rxcon); + if (value) { + phytium_write8(&priv->regs->ep[epNum - 1].rxcon, rxcon | CON_STALL); + phytium_write8(&priv->regs->endprst, ENDPRST_IO_TX | epNum); + phytium_write8(&priv->regs->endprst, epNum | ENDPRST_FIFORST); + } else { + phytium_write8(&priv->regs->endprst, ENDPRST_IO_TX | epNum); + phytium_write8(&priv->regs->endprst, + epNum | ENDPRST_TOGRST | ENDPRST_FIFORST); + phytium_write8(&priv->regs->ep[epNum - 1].rxcon, rxcon & (~CON_STALL)); + } + } + + if (gadgetEp->state != GADGET_EP_BUSY && !value && req) { + gadgetRequest = requestToGadgetRequest(req); + if (gadgetEp->isInEp) + gadgetEpXDataSend(priv, gadgetRequest); + else + gadgetEpXDataReceive(priv, gadgetRequest); + } + + return 0; +} + +static int32_t gadgetEp0SetHalt(struct GADGET_CTRL *priv, struct GADGET_EP *gadget_Ep, + uint8_t value) +{ + struct GadgetEp *gadgetEp; + + if (!priv || !gadget_Ep) + return -EINVAL; + + gadgetEp = toGadgetEp(gadget_Ep); + if (!list_empty(&gadgetEp->request)) + return -EBUSY; + + switch (priv->ep0State) { + case GADGET_EP0_STAGE_IN: + case GADGET_EP0_STAGE_OUT: + case GADGET_EP0_STAGE_ACK: + case GADGET_EP0_STAGE_STATUSIN: + case GADGET_EP0_STAGE_STATUSOUT: + priv->ep0State = GADGET_EP0_STAGE_SETUP; + break; + default: + return -EINVAL; + } + + return 0; +} + + +static void gadgetEp0Callback(struct GADGET_CTRL *priv, struct GadgetEp *gadgetEp, + struct GADGET_REQ *req, uint8_t status) +{ + if (!priv || !gadgetEp || !req) + return; + + priv->ep0State = GADGET_EP0_STAGE_SETUP; + list_del(&req->list); + gadgetEp->requestsInList--; + + if (req->status == EINPROGRESS) + req->status = status; + + if (req->complete) + req->complete(&gadgetEp->gadgetEp, req); +} + +static enum usb_device_speed gadgetGetActualSpeed(struct GADGET_CTRL *priv) +{ + uint8_t speedctrl; + + if (!priv) + return USB_SPEED_UNKNOWN; + + speedctrl = phytium_read8(&priv->regs->speedctrl) & (~SPEEDCTRL_HSDISABLE); + switch (speedctrl) { + case SPEEDCTRL_HS: + return USB_SPEED_HIGH; + case SPEEDCTRL_FS: + return USB_SPEED_FULL; + case SPEEDCTRL_LS: + return USB_SPEED_LOW; + default: + return USB_SPEED_UNKNOWN; + } +} + +static int32_t gadgetServiceSetFeatureReq(struct GADGET_CTRL *priv, struct usb_ctrlrequest *setup) +{ + uint8_t epNum, isIn; + struct GadgetEp *gadgetEp; + + if (!priv || !setup) + return 0; + + switch (setup->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + switch (setup->wValue) { + case USB_DEVICE_REMOTE_WAKEUP: + pr_info("set feature - remote wakup\n"); + priv->isRemoteWakeup = 1; + break; + case USB_DEVICE_B_HNP_ENABLE: + pr_info("set feature - B HNP Enable\n"); + pr_info("otg not implement\n"); + return -EINVAL; + case USB_DEVICE_A_HNP_SUPPORT: + pr_info("set feature - A HNP support\n"); + pr_info("otg not implete\n"); + return -EINVAL; + } + break; + case USB_RECIP_INTERFACE: + break; + case USB_RECIP_ENDPOINT: + epNum = setup->wIndex & 0x0f; + isIn = setup->wIndex & USB_DIR_IN; + if (epNum == 0 || epNum > 15 || setup->wValue != 0) + return -EINVAL; + + gadgetEp = isIn ? &priv->in[epNum] : &priv->out[epNum]; + + gadgetEpXSetHalt(priv, &gadgetEp->gadgetEp, 1); + break; + default: + return -EINVAL; + } + return 0; +} + +static int32_t gadgetServiceClearFeatureReq(struct GADGET_CTRL *priv, struct usb_ctrlrequest *setup) +{ + uint8_t epNum, isIn; + struct GadgetEp *gadgetEp; + + if (!priv || !setup) + return -EINVAL; + + switch (setup->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + if (setup->wValue == USB_DEVICE_B_HNP_ENABLE) { + pr_err("otg not implement\n"); + return -EINVAL; + } + + if (setup->wValue != USB_DEVICE_REMOTE_WAKEUP) + return GADGET_EUNHANDLED; + + priv->isRemoteWakeup = 0; + return GADGET_EAUTOACK; + case USB_RECIP_INTERFACE: + break; + case USB_RECIP_ENDPOINT: + pr_info("clear feature wIndex:0x%x wValue:0x%x\n", setup->wIndex, setup->wValue); + epNum = setup->wIndex & 0x7f; + isIn = setup->wIndex & USB_DIR_IN; + if (epNum == 0 || epNum > 15 || setup->wValue != 0) + return GADGET_EUNHANDLED; + + gadgetEp = isIn ? &priv->in[epNum] : &priv->out[epNum]; + if (!gadgetEp->gadgetEp.desc) + return -EINVAL; + + if (gadgetEp->wedged) + break; + gadgetEpXSetHalt(priv, &gadgetEp->gadgetEp, 0); + break; + default: + return GADGET_EUNHANDLED; + } + + return 0; +} + +static int32_t gadgetServiceSetupReq(struct GADGET_CTRL *priv, struct usb_ctrlrequest *setup) +{ + struct GadgetEp *gadgetEp; + uint8_t isIn, epNum; + uint8_t rxcon, txcon; + int len; + + if (!priv || !setup) + return -EINVAL; + + if ((setup->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { + if (setup->bRequest != USB_REQ_GET_STATUS) + return GADGET_EUNHANDLED; + } else + return GADGET_EUNHANDLED; + + priv->privBuffAddr[1] = 0; + priv->privBuffAddr[0] = 0; + + switch (setup->bRequest & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + pr_info("wIndex:0x%x isSelfPowered:%d isRomoteWakeup:%d\n", + setup->wIndex, priv->isSelfPowered, priv->isRemoteWakeup); + + if (setup->wIndex == OTG_STS_SELECTOR) + priv->privBuffAddr[0] = priv->hostRequestFlag; + else { + priv->privBuffAddr[0] = priv->isSelfPowered ? USB_DEVICE_SELF_POWERED : 0; + priv->privBuffAddr[1] = priv->isRemoteWakeup ? USB_DEVICE_REMOTE_WAKEUP : 0; + } + break; + case USB_RECIP_INTERFACE: + break; + case USB_RECIP_ENDPOINT: + epNum = setup->wIndex & 0x0f; + if (!epNum) + break; + + isIn = setup->wIndex & USB_DIR_IN; + + gadgetEp = isIn ? &priv->in[epNum] : &priv->out[epNum]; + if (!gadgetEp->gadgetEp.desc) + return -EINVAL; + + if (isIn) { + txcon = phytium_read8(&priv->regs->ep[epNum - 1].txcon); + priv->privBuffAddr[0] = (txcon & CON_STALL) ? 1 : 0; + } else { + rxcon = phytium_read8(&priv->regs->ep[epNum - 1].rxcon); + priv->privBuffAddr[0] = (rxcon & CON_STALL) ? 1 : 0; + } + break; + default: + return GADGET_EUNHANDLED; + } + + len = setup->wLength; + if (len > 2) + len = 2; + + priv->dmaDrv->dma_channelProgram(priv->dmaController, priv->in[0].channel, + priv->in[0].gadgetEp.maxPacket, priv->privBuffDma, len, NULL, 0); + + return 0; +} + +static int32_t gadgetGetSetup(struct GADGET_CTRL *priv, struct usb_ctrlrequest *setup) +{ + int i; + uint8_t ep0cs; + struct GADGET_REQ *request = NULL; + struct GadgetRequest *gadgetRequest; + + if (!priv || !setup) + return -EINVAL; + + phytium_write8(&priv->regs->ep0cs, EP0CS_CHGSET); + + for (i = 0; i < 8; i++) + ((char *)setup)[i] = phytium_read8(&priv->regs->setupdat[i]); + + ep0cs = phytium_read8(&priv->regs->ep0cs); + if (ep0cs & EP0CS_CHGSET) { + pr_info("setup flags change: not error\n"); + return GADGET_EAUTOACK; + } + + phytium_write8(&priv->regs->usbirq, USBIR_SUDAV); + pr_debug("setup packet: req%02x.%02x v:%04x i:%04x I%d\n", setup->bRequestType, + setup->bRequest, setup->wValue, setup->wIndex, setup->wLength); + + request = gadgetGetNextEp0Req(priv); + if (request) { + gadgetRequest = requestToGadgetRequest(request); + pr_info("Previous request has not been finished but new was received\n"); + gadgetEp0Callback(priv, gadgetRequest->ep, request, 0); + } + + if (setup->wLength) { + if (setup->bRequestType & USB_DIR_IN) + priv->ep0State = GADGET_EP0_STAGE_IN; + else + priv->ep0State = GADGET_EP0_STAGE_OUT; + } else + priv->ep0State = GADGET_EP0_STAGE_ACK; + + return 0; +} + +static void gadgetEp0StageSetup(struct GADGET_CTRL *priv) +{ + struct usb_ctrlrequest setup; + int32_t retval; + uint8_t ep0cs; + + if (!priv) + return; + + retval = gadgetGetSetup(priv, &setup); + + priv->gadgetDev.speed = gadgetGetActualSpeed(priv); + + switch (priv->ep0State) { + case GADGET_EP0_STAGE_ACK: + if ((setup.bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD) + retval = GADGET_EUNHANDLED; + else { + switch (setup.bRequest) { + case USB_REQ_SET_ADDRESS: + priv->deviceAddress = setup.wValue & 0x7F; + priv->gadgetDev.state = USB_STATE_ADDRESS; + pr_info("set address: %d\n", priv->deviceAddress); + retval = GADGET_EAUTOACK; + break; + case USB_REQ_SET_FEATURE: + retval = gadgetServiceSetFeatureReq(priv, &setup); + break; + case USB_REQ_CLEAR_FEATURE: + retval = gadgetServiceClearFeatureReq(priv, &setup); + break; + default: + retval = GADGET_EUNHANDLED; + break; + } + } + + if (retval == GADGET_EUNHANDLED) + break; + else if (retval == 0) + phytium_write8(&priv->regs->ep0cs, EP0CS_HSNAK); + + priv->ep0State = GADGET_EP0_STAGE_SETUP; + break; + case GADGET_EP0_STAGE_IN: + pr_debug("setup data stage in\n"); + retval = gadgetServiceSetupReq(priv, &setup); + + if (retval == 0) + priv->ep0State = GADGET_EP0_STAGE_STATUSOUT; + break; + case GADGET_EP0_STAGE_OUT: + pr_debug("setup data stage out\n"); + phytium_write8(&priv->regs->ep0Rxbc, 0); + retval = GADGET_EUNHANDLED; + break; + default: + if (retval == GADGET_EAUTOACK) + return; + + pr_debug("forward request\n"); + retval = GADGET_EUNHANDLED; + break; + } + + if (retval == GADGET_EUNHANDLED) { + if (priv->eventCallback.setup) + retval = priv->eventCallback.setup(priv, &setup); + + if (retval == 0x7FFF) { + pr_debug("Respond Delayed not finished yet\n"); + return; + } + + if (retval) + retval = GADGET_ESTALL; + } + + if (retval == GADGET_EUNHANDLED || retval == GADGET_ESTALL) { + pr_debug("request not handled - send stall\n"); + ep0cs = phytium_read8(&priv->regs->ep0cs); + ep0cs |= EP0CS_STALL; + phytium_write8(&priv->regs->ep0cs, ep0cs); + priv->ep0State = GADGET_EP0_STAGE_SETUP; + } else if (priv->ep0State == GADGET_EP0_STAGE_ACK) { + priv->ep0State = GADGET_EP0_STAGE_SETUP; + phytium_write8(&priv->regs->ep0cs, EP0CS_HSNAK); + pr_debug("setup transfer completed\n"); + } +} +static void gadgetEp0DataSend(struct GADGET_CTRL *priv) +{ + struct GADGET_REQ *request; + uint32_t chMaxLen, requestSize; + + if (!priv) + return; + + request = gadgetGetNextEp0Req(priv); + if (!request) { + pr_debug("Ep0 queue is empty\n"); + return; + } + + if (priv->dmaDrv->dma_getChannelStatus(priv->dmaController, priv->in[0].channel) + >= DMA_STATUS_BUSY) { + pr_err("transfer is pending now\n"); + return; + } + + chMaxLen = priv->dmaDrv->dma_getMaxLength(priv->dmaController, priv->in[0].channel); + requestSize = (request->length < chMaxLen) ? request->length : chMaxLen; + pr_debug("usbRequest;%p requestSize:%d packetSize:%d\n", request, requestSize, + priv->in[0].gadgetEp.maxPacket); + priv->ep0State = GADGET_EP0_STAGE_STATUSOUT; + + priv->dmaDrv->dma_channelProgram(priv->dmaController, priv->in[0].channel, + priv->in[0].gadgetEp.maxPacket, request->dma, requestSize, NULL, 0); +} + +static void gadgetEp0DataReceive(struct GADGET_CTRL *priv) +{ + uint32_t chMaxLen, requestSize; + struct GADGET_REQ *request; + + if (!priv) + return; + + request = gadgetGetNextEp0Req(priv); + if (!request) { + pr_debug("Ep0 queue is empty\n"); + return; + } + + chMaxLen = priv->dmaDrv->dma_getMaxLength(priv->dmaController, priv->out[0].channel); + requestSize = (request->length < chMaxLen) ? request->length : chMaxLen; + pr_debug("usbRequest;%p requestSize:%d packetSize:%d\n", request, requestSize, + priv->out[0].gadgetEp.maxPacket); + priv->ep0State = GADGET_EP0_STAGE_STATUSIN; + + priv->dmaDrv->dma_channelProgram(priv->dmaController, priv->out[0].channel, + priv->out[0].gadgetEp.maxPacket, request->dma, requestSize, NULL, 0); +} + +static uint32_t gadgetEp0Irq(struct GADGET_CTRL *priv) +{ + uint8_t usbcs; + struct GADGET_REQ *request; + + if (!priv) + return 0; + + switch (priv->ep0State) { + case GADGET_EP0_STAGE_IN://send data + pr_debug("DATA Stage IN\n"); + gadgetEp0DataSend(priv); + break; + case GADGET_EP0_STAGE_OUT://receive data + pr_debug("DATA Stage OUT\n"); + gadgetEp0DataReceive(priv); + break; + case GADGET_EP0_STAGE_STATUSIN: + pr_debug("DATA Stage STATUS IN\n"); + request = gadgetGetNextEp0Req(priv); + if (request) + request->actual = priv->dmaDrv->dma_getActualLength(priv->dmaController, + priv->out[0].channel); + + priv->ep0State = GADGET_EP0_STAGE_SETUP; + phytium_write8(&priv->regs->ep0cs, EP0CS_HSNAK); + gadgetEp0Callback(priv, &priv->out[0], request, 0); + break; + case GADGET_EP0_STAGE_STATUSOUT: + pr_debug("DATA Stage STATUS OUT\n"); + request = gadgetGetNextEp0Req(priv); + if (request) + request->actual = priv->dmaDrv->dma_getActualLength(priv->dmaController, + priv->in[0].channel); + + phytium_write8(&priv->regs->ep0cs, EP0CS_HSNAK); + if (request) + gadgetEp0Callback(priv, &priv->in[0], request, 0); + else + priv->ep0State = GADGET_EP0_STAGE_SETUP; + break; + case GADGET_EP0_STAGE_SETUP: + pr_debug("DATA Stage SETUP\n"); + gadgetEp0StageSetup(priv); + break; + case GADGET_EP0_STAGE_ACK: + pr_debug("DATA Stage ACK\n"); + break; + default: + pr_debug("DATA Stage UNKNOWN\n"); + usbcs = phytium_read8(&priv->regs->usbcs); + usbcs |= EP0CS_STALL; + phytium_write8(&priv->regs->usbcs, usbcs); + break; + } + + return 0; +} + +static void gadgetEpXCallback(struct GADGET_CTRL *priv, struct GadgetEp *gadgetEp, + struct GADGET_REQ *req, uint32_t status) +{ + if (!gadgetEp || !req) + return; + + list_del(&req->list); + gadgetEp->requestsInList--; + req->status = status; + + if (req->complete) + req->complete(&gadgetEp->gadgetEp, req); +} + +static void gadgetEpXDataCallback(struct GADGET_CTRL *priv, uint8_t epNum, uint8_t epDir) +{ + struct GadgetEp *gadgetEp; + struct GADGET_REQ *gadgetReq; + uint32_t actual_length = 0; + uint32_t chMaxLen = 0; + uint8_t epType; + + if (!priv) + return; + + pr_debug("%s %d epNum:%d epDir:%d\n", __func__, __LINE__, epNum, epDir); + gadgetEp = epDir ? &priv->in[epNum] : &priv->out[epNum]; + + gadgetReq = gadgetGetNextReq(gadgetEp); + if (!gadgetReq) { + pr_debug("%s queue is empty\n", gadgetEp->gadgetEp.name); + return; + } + + if (gadgetEp->channel) { + actual_length = priv->dmaDrv->dma_getActualLength(priv->dmaController, + gadgetEp->channel); + chMaxLen = priv->dmaDrv->dma_getMaxLength(priv->dmaController, gadgetEp->channel); + gadgetReq->actual += actual_length; + } + + if (gadgetReq->actual == gadgetReq->length || actual_length < chMaxLen) { + gadgetEpXCallback(priv, gadgetEp, gadgetReq, 0); + + if (gadgetEp->gadgetEp.desc) { + epType = gadgetEp->gadgetEp.desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + if (epType == USB_ENDPOINT_XFER_ISOC) + return; + + gadgetReq = gadgetGetNextReq(gadgetEp); + gadgetEp->state = GADGET_EP_ALLOCATED; + if (!gadgetReq) { + pr_debug("%s queue is empty\n", gadgetEp->gadgetEp.name); + return; + } + gadgetEp->state = GADGET_EP_BUSY; + if (epDir) + gadgetEpXDataSend(priv, requestToGadgetRequest(gadgetReq)); + else + gadgetEpXDataReceive(priv, requestToGadgetRequest(gadgetReq)); + } + } else { + if (epDir) + gadgetEpXDataSend(priv, requestToGadgetRequest(gadgetReq)); + else + gadgetEpXDataReceive(priv, requestToGadgetRequest(gadgetReq)); + } +} + +void gadget_CallbackTransfer(void *priv, uint8_t epNum, uint8_t epDir, bool resubmit) +{ + if (!epNum) + gadgetEp0Irq(priv); + else + gadgetEpXDataCallback(priv, epNum, epDir); +} + +static void gadgetAbortEndpoint(struct GADGET_CTRL *priv, struct GadgetEp *gadgetEp) +{ + struct GADGET_REQ *gadgetReq; + + pr_debug("Abort Device endpoint: %s, dma channel: %p\n", gadgetEp->gadgetEp.name, + gadgetEp->channel); + + if (gadgetEp->channel && gadgetEp->hwEpNum != 0) { + priv->dmaDrv->dma_channelRelease(priv->dmaController, gadgetEp->channel); + gadgetEp->channel = NULL; + } + + if (gadgetEp->channel && gadgetEp->hwEpNum == 0) { + if (priv->releaseEp0Flag == 1) { + priv->dmaDrv->dma_channelAbort(priv->dmaController, gadgetEp->channel); + priv->dmaDrv->dma_channelAlloc(priv->dmaController, gadgetEp->isInEp, + gadgetEp->hwEpNum, 0); + } + } + + while (gadgetEp->request.next != &gadgetEp->request) { + gadgetReq = listToGadgetRequest(gadgetEp->request.next); + pr_debug("shutdown request %p form epName:%s\n", gadgetReq, + gadgetEp->gadgetEp.name); + if (!gadgetEp->gadgetEp.address) + gadgetEp0Callback(priv, gadgetEp, gadgetReq, GADGET_ESHUTDOWN); + else + gadgetEpXCallback(priv, gadgetEp, gadgetReq, GADGET_ESHUTDOWN); + } + + gadgetEp->state = GADGET_EP_FREE; +} + +static void gadgetStopActivity(struct GADGET_CTRL *priv) +{ + int i = 0; + struct GadgetEp *gadgetEp; + + pr_debug("USB Stop Activity\n"); + + if (!priv) + return; + + for (i = 0; i < 16; i++) { + gadgetEp = &priv->in[i]; + if (gadgetEp->state != GADGET_EP_NOT_IMPLEMENTED) + gadgetAbortEndpoint(priv, gadgetEp); + + gadgetEp = &priv->out[i]; + if (gadgetEp->state != GADGET_EP_NOT_IMPLEMENTED) + gadgetAbortEndpoint(priv, gadgetEp); + } +} + + +static int32_t gadgetEp0Enable(struct GADGET_CTRL *priv, struct GadgetEp *gadgetEp) +{ + uint8_t txien, rxien; + + if (!priv || !gadgetEp) + return -EINVAL; + + if (gadgetEp->state != GADGET_EP_FREE) + return -EBUSY; + + if (!gadgetEp->isInEp) { + rxien = phytium_read16(&priv->regs->rxien); + rxien &= ~(1 << gadgetEp->hwEpNum); + phytium_write16(&priv->regs->rxien, rxien); + phytium_write8(&priv->regs->ep0fifoctrl, FIFOCTRL_IO_TX | FIFOCTRL_FIFOAUTO); + } else { + txien = phytium_read16(&priv->regs->txien); + txien &= ~(1 << gadgetEp->hwEpNum); + phytium_write16(&priv->regs->txien, txien); + phytium_write8(&priv->regs->ep0fifoctrl, FIFOCTRL_FIFOAUTO); + } + + gadgetEp->gadgetEp.desc = NULL; + gadgetEp->state = GADGET_EP_ALLOCATED; + gadgetEp->channel = priv->dmaDrv->dma_channelAlloc(priv->dmaController, + gadgetEp->isInEp, gadgetEp->hwEpNum, 0); + phytium_write8(&priv->regs->ep0maxpack, 0x40); + + return 0; +} + +static int32_t gadgetEpXEnable(struct GADGET_CTRL *priv, struct GadgetEp *gadgetEp, + const struct usb_endpoint_descriptor *desc) +{ + uint32_t status = -EINVAL; + uint32_t payload; + uint16_t type, iso = 0; + uint8_t epNum = 0; + + if (!priv || !gadgetEp || !desc) + return -EINVAL; + + pr_debug("enable endpoint %s\n", gadgetEp->gadgetEp.name); + if (gadgetEp->state != GADGET_EP_FREE) { + status = -EBUSY; + goto fail; + } + + payload = desc->wMaxPacketSize & 0x7ff; + if (!payload) { + status = -EINVAL; + goto fail; + } + + epNum = gadgetEp->hwEpNum; + + type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + switch (type) { + case USB_ENDPOINT_XFER_ISOC: + type = CON_TYPE_ISOC; + switch (payload >> 11) { + case 0: + iso = CON_TYPE_ISOC_1_ISOD; + break; + case 1: + payload *= 2; + iso = CON_TYPE_ISOC_2_ISOD; + break; + case 2: + payload *= 3; + iso = CON_TYPE_ISOC_3_ISOD; + break; + } + break; + case USB_ENDPOINT_XFER_INT: + type = CON_TYPE_INT; + break; + case USB_ENDPOINT_XFER_BULK: + type = CON_TYPE_BULK; + break; + } + + if (desc->bEndpointAddress & USB_DIR_IN) { + if (!gadgetEp->isInEp) { + status = -ENODEV; + goto fail; + } + + if (payload > priv->gadgetCfg.epIN[epNum].maxPacketSize) { + status = -EINVAL; + goto fail; + } + + phytium_write16(&priv->regs->txmaxpack[epNum - 1], payload); + phytium_write8(&priv->regs->ep[epNum - 1].txcon, CON_VAL | type + | iso | (priv->gadgetCfg.epIN[epNum - 1].bufferingValue - 1)); + + phytium_write8(&priv->regs->fifoctrl, FIFOCTRL_FIFOAUTO | FIFOCTRL_IO_TX | epNum); + phytium_write8(&priv->regs->endprst, ENDPRST_IO_TX | epNum); + phytium_write8(&priv->regs->endprst, ENDPRST_IO_TX | epNum | + ENDPRST_FIFORST | ENDPRST_TOGRST); + } else { + if (gadgetEp->isInEp) { + status = -ENODEV; + goto fail; + } + + if (payload > priv->gadgetCfg.epOUT[epNum].maxPacketSize) { + status = -EINVAL; + goto fail; + } + + phytium_write16(&priv->regs->rxmaxpack[epNum - 1], payload); + phytium_write8(&priv->regs->ep[epNum - 1].rxcon, CON_VAL | type + | iso | (priv->gadgetCfg.epIN[epNum - 1].bufferingValue - 1)); + + phytium_write8(&priv->regs->fifoctrl, FIFOCTRL_FIFOAUTO | epNum); + phytium_write8(&priv->regs->endprst, epNum); + phytium_write8(&priv->regs->endprst, epNum | ENDPRST_FIFORST | ENDPRST_TOGRST); + } + + if (priv->dmaController) + gadgetEp->channel = priv->dmaDrv->dma_channelAlloc(priv->dmaController, + gadgetEp->isInEp, epNum, (type == CON_TYPE_ISOC) ? 1 : 0); + + if (type == CON_TYPE_ISOC) { + if (gadgetEp->isInEp) { + phytium_write16(&priv->regs->isoautodump, 1 << epNum); + phytium_write16(&priv->regs->isodctrl, 1 << epNum); + } + + priv->dmaDrv->dma_setMaxLength(priv->dmaController, gadgetEp->channel, payload); + } + + gadgetEp->state = GADGET_EP_ALLOCATED; + gadgetEp->gadgetEp.desc = desc; +fail: + return status; +} + +static int32_t gadgetEpEnable(struct GADGET_CTRL *priv, struct GADGET_EP *gadget_Ep, + const struct usb_endpoint_descriptor *desc) +{ + struct GadgetEp *gadgetEp = NULL; + + if (!priv || !gadget_Ep || !desc) + return -EINVAL; + + gadgetEp = toGadgetEp(gadget_Ep); + gadgetEp->wedged = 0; + + if (gadgetEp->hwEpNum) + return gadgetEpXEnable(priv, gadgetEp, desc); + else + return gadgetEp0Enable(priv, gadgetEp); +} + +static int32_t gadgetEpXDisable(struct GADGET_CTRL *priv, struct GadgetEp *gadgetEp) +{ + uint8_t txcon, rxcon; + + if (!priv || !gadgetEp) + return -EINVAL; + + pr_debug("disable endpoint %s\n", gadgetEp->gadgetEp.name); + if (gadgetEp->isInEp) { + txcon = phytium_read8(&priv->regs->ep[gadgetEp->hwEpNum - 1].txcon); + txcon &= ~CON_VAL; + phytium_write8(&priv->regs->ep[gadgetEp->hwEpNum - 1].txcon, txcon); + } else { + rxcon = phytium_read8(&priv->regs->ep[gadgetEp->hwEpNum - 1].rxcon); + rxcon &= ~CON_VAL; + phytium_write8(&priv->regs->ep[gadgetEp->hwEpNum - 1].rxcon, rxcon); + } + gadgetAbortEndpoint(priv, gadgetEp); + gadgetEp->gadgetEp.desc = 0; + gadgetEp->state = GADGET_EP_FREE; + return 0; +} + +static int32_t gadgetEp0Disable(struct GADGET_CTRL *priv, struct GadgetEp *gadgetEp) +{ + if (!priv || !gadgetEp) + return -EINVAL; + + pr_debug("disable endpoint %s\n", gadgetEp->gadgetEp.name); + gadgetAbortEndpoint(priv, gadgetEp); + + return 0; +} + +static int32_t gadgetEpDisable(struct GADGET_CTRL *priv, struct GADGET_EP *gadget_Ep) +{ + struct GadgetEp *gadgetEp = NULL; + + if (!priv || !gadget_Ep) + return -EINVAL; + + if (gadget_Ep->address == 0) + return -EINVAL; + + gadgetEp = toGadgetEp(gadget_Ep); + gadgetEp->wedged = 0; + if (gadgetEp->hwEpNum) + return gadgetEpXDisable(priv, gadgetEp); + else + return gadgetEp0Disable(priv, gadgetEp); +} + +static int32_t gadgetEpXQueue(struct GADGET_CTRL *priv, struct GadgetEp *gadgetEp, + struct GADGET_REQ *req) +{ + struct GadgetRequest *gadgetRequest; + + if (!priv || !gadgetEp || !req) + return -EINVAL; + + req->actual = 0; + req->status = EINPROGRESS; + + gadgetRequest = requestToGadgetRequest(req); + gadgetRequest->ep = gadgetEp; + + if (req->length == 0) + gadgetRequest->zlp = 1; + + if (gadgetEp->gadgetEp.desc == NULL) { + pr_info("%s is disabled - can not queue request %p\n", + gadgetEp->gadgetEp.name, req); + return -EINVAL; + } + + list_add_tail(&req->list, &gadgetEp->request); + pr_debug("queue to %s (%s), length:%d\n", gadgetEp->gadgetEp.name, + (gadgetEp->isInEp ? "IN/TX" : "OUT/RX"), req->length); + + if ((gadgetEp->state == GADGET_EP_ALLOCATED) && (&req->list == gadgetEp->request.next)) { + if (gadgetEp->isInEp) { + if (!(phytium_read8(&priv->regs->ep[gadgetEp->hwEpNum - 1].txcon) + & CON_STALL)) { + gadgetEp->state = GADGET_EP_BUSY; + gadgetEpXDataSend(priv, gadgetRequest); + } + } else { + if (!(phytium_read8(&priv->regs->ep[gadgetEp->hwEpNum - 1].rxcon) + & CON_STALL)) { + gadgetEp->state = GADGET_EP_BUSY; + gadgetEpXDataReceive(priv, gadgetRequest); + } + } + } else if (gadgetEp->state == GADGET_EP_BUSY) { + if (usb_endpoint_xfer_isoc(gadgetEp->gadgetEp.desc)) { + if (gadgetEp->isInEp) + gadgetEpXDataSend(priv, gadgetRequest); + else + gadgetEpXDataReceive(priv, gadgetRequest); + } + } + + pr_debug("endpoint %s (%s) now is busy - transfer will be waiting in Queue\n", + gadgetEp->gadgetEp.name, (gadgetEp->isInEp ? "IN/TX" : "OUT/RX")); + + return 0; +} + +static int32_t gadgetEp0Queue(struct GADGET_CTRL *priv, struct GadgetEp *gadgetEp, + struct GADGET_REQ *req) +{ + struct GadgetRequest *gadgetRequest; + + if (!priv || !gadgetEp || !req) + return -EINVAL; + + req->actual = 0; + req->status = EINPROGRESS; + + if (!list_empty(&gadgetEp->request)) + return -EBUSY; + + gadgetRequest = requestToGadgetRequest(req); + gadgetRequest->ep = gadgetEp; + + switch (priv->ep0State) { + case GADGET_EP0_STAGE_OUT: + case GADGET_EP0_STAGE_IN: + case GADGET_EP0_STAGE_ACK: + break; + default: + return -EINVAL; + } + + list_add_tail(&req->list, &gadgetEp->request); + gadgetEp->requestsInList++; + + pr_debug("queue to %s (%s), length:%d stage:%d\n", gadgetEp->gadgetEp.name, + gadgetEp->isInEp ? "IN/TX" : "OUT/RX", req->length, priv->ep0State); + + switch (priv->ep0State) { + case GADGET_EP0_STAGE_OUT: + gadgetEp0DataReceive(priv); + break; + case GADGET_EP0_STAGE_IN: + gadgetEp0DataSend(priv); + break; + case GADGET_EP0_STAGE_ACK: + if (req->length) + return -EINVAL; + phytium_write8(&priv->regs->ep0cs, EP0CS_HSNAK); + gadgetEp0Callback(priv, gadgetRequest->ep, req, 0); + pr_info("control transfer completed\n"); + break; + default: + break; + } + + return 0; +} + +static int32_t gadgetEpQueue(struct GADGET_CTRL *priv, struct GADGET_EP *gadget_Ep, + struct GADGET_REQ *req) +{ + struct GadgetEp *gadgetEp = NULL; + + if (!priv || !gadget_Ep || !req) + return -EINVAL; + + gadgetEp = toGadgetEp(gadget_Ep); + + if (gadget_Ep->address & GADGET_USB_EP_NUMBER_MASK) + return gadgetEpXQueue(priv, gadgetEp, req); + else + return gadgetEp0Queue(priv, gadgetEp, req); +} + +static int32_t gadgetEpXDequeue(struct GADGET_CTRL *priv, struct GadgetEp *gadgetEp, + struct GADGET_REQ *req) +{ + struct GadgetRequest *gadgetRequest; + struct GADGET_REQ *iterator; + + if (!priv || !gadgetEp || !req) + return -EINVAL; + + gadgetRequest = requestToGadgetRequest(req); + if (gadgetRequest->ep != gadgetEp) + return -EINVAL; + + pr_debug("Dequeue request %p form %s\n", req, gadgetEp->gadgetEp.name); + + listBrowsingRequest(iterator, &gadgetEp->request, list) { + if (req == iterator) + break; + } + + if (req != iterator) { + pr_info("request %p not queued to %s\n", req, gadgetEp->gadgetEp.name); + return -EINVAL; + } + + if (gadgetEp->state == GADGET_EP_BUSY) { + priv->dmaDrv->dma_channelAbort(priv->dmaController, gadgetEp->channel); + gadgetEp->state = GADGET_EP_ALLOCATED; + } + + gadgetEpXCallback(priv, gadgetEp, req, GADGET_ECONNRESET); + + return 0; +} + +static int32_t gadgetEp0Dequeue(struct GADGET_CTRL *priv, struct GadgetEp *gadgetEp, + struct GADGET_REQ *req) +{ + return 0; +} + +static int32_t gadgetEpDequeue(struct GADGET_CTRL *priv, struct GADGET_EP *gadget_Ep, + struct GADGET_REQ *req) +{ + struct GadgetEp *gadgetEp = NULL; + + if (!priv || !gadget_Ep || !req) + return -EINVAL; + + gadgetEp = toGadgetEp(gadget_Ep); + + if (gadget_Ep->address & GADGET_USB_EP_NUMBER_MASK) + return gadgetEpXDequeue(priv, gadgetEp, req); + else + return gadgetEp0Dequeue(priv, gadgetEp, req); +} + +static int32_t gadgetEpSetHalt(struct GADGET_CTRL *priv, struct GADGET_EP *gadget_Ep, uint8_t value) +{ + if (!priv || !gadget_Ep) + return -EINVAL; + + if (gadget_Ep->address & GADGET_USB_EP_NUMBER_MASK) + return gadgetEpXSetHalt(priv, gadget_Ep, value); + else + return gadgetEp0SetHalt(priv, gadget_Ep, value); +} + +static int32_t gadgetEpSetWedge(struct GADGET_CTRL *priv, struct GADGET_EP *gadget_Ep) +{ + struct GadgetEp *gadgetEp = NULL; + + if (!priv || !gadget_Ep) + return -EINVAL; + + gadgetEp = toGadgetEp(gadget_Ep); + gadgetEp->wedged = 1; + + return gadgetEpSetHalt(priv, gadget_Ep, 1); +} + +static int32_t gadgetEpFifoStatus(struct GADGET_CTRL *priv, struct GADGET_EP *gadget_Ep) +{ + if (!priv || !gadget_Ep) + return -EINVAL; + + return 0; +} + +static void gadgetEpFifoFlush(struct GADGET_CTRL *priv, struct GADGET_EP *gadget_Ep) +{ + if (!priv || !gadget_Ep) + return; +} + +static int32_t gadgetEpAllocRequest(struct GADGET_CTRL *priv, struct GADGET_EP *gadget_Ep, + struct GADGET_REQ **req) +{ + struct GadgetEp *gadgetEp = NULL; + struct GadgetRequest *gadgetRequest = NULL; + + if (!priv || !gadget_Ep || !req) + return -EINVAL; + + gadgetEp = toGadgetEp(gadget_Ep); + if (priv->eventCallback.usbRequestMemAlloc) + gadgetRequest = priv->eventCallback.usbRequestMemAlloc(priv, + sizeof(*gadgetRequest)); + if (!gadgetRequest) + return -ENOMEM; + + memset(gadgetRequest, 0, sizeof(*gadgetRequest)); + *req = &gadgetRequest->request; + INIT_LIST_HEAD(&gadgetRequest->request.list); + gadgetRequest->ep = gadgetEp; + + return 0; +} + +static void gadgetEpFreeRequest(struct GADGET_CTRL *priv, struct GADGET_EP *gadget_Ep, + struct GADGET_REQ *req) +{ + struct GadgetRequest *gadgetRequest = NULL; + + if (!priv || !gadget_Ep || !req) + return; + + gadgetRequest = requestToGadgetRequest(req); + + if (priv->eventCallback.usbRequestMemFree) + priv->eventCallback.usbRequestMemFree(priv, gadgetRequest); +} + +static struct GADGET_EP_OPS gadgetEpOps = { + .epEnable = gadgetEpEnable, + .epDisable = gadgetEpDisable, + .reqQueue = gadgetEpQueue, + .reqDequeue = gadgetEpDequeue, + .epSetHalt = gadgetEpSetHalt, + .epSetWedge = gadgetEpSetWedge, + .epFifoStatus = gadgetEpFifoStatus, + .reqAlloc = gadgetEpAllocRequest, + .reqFree = gadgetEpFreeRequest +}; + +static void gadgetInitDeviceEp(struct GADGET_CTRL *priv, uint8_t isInEp) +{ + uint8_t num; + struct GadgetEp *gadgetEp; + struct GADGET_EP_CFG epCfg; + + if (!priv) + return; + + for (num = 0; num < 16; num++) { + gadgetEp = isInEp ? &priv->in[num] : &priv->out[num]; + if (num) { + epCfg = isInEp ? priv->gadgetCfg.epIN[num] : priv->gadgetCfg.epOUT[num]; + if (!epCfg.bufferingValue) { + gadgetEp->state = GADGET_EP_NOT_IMPLEMENTED; + gadgetEp->hwEpNum = num; + continue; + } + } + INIT_LIST_HEAD(&gadgetEp->gadgetEp.epList); + INIT_LIST_HEAD(&gadgetEp->request); + gadgetEp->state = GADGET_EP_FREE; + gadgetEp->hwEpNum = num; + gadgetEp->isInEp = isInEp; + gadgetEp->requestsInList = 0; + snprintf(gadgetEp->gadgetEp.name, sizeof(gadgetEp->gadgetEp.name), + "Ep%d%s", num, isInEp ? "in" : "out"); + if (!num) { + gadgetEp->gadgetEp.maxPacket = priv->gadgetCfg.epIN[num].maxPacketSize; + if (isInEp) + priv->gadgetDev.ep0 = &gadgetEp->gadgetEp; + gadgetEp->gadgetEp.ops = &gadgetEpOps; + gadgetEp0Enable(priv, gadgetEp); + continue; + } + + if (isInEp) { + if (epCfg.startBuf) + phytium_write16(&priv->regs->txstaddr[num - 1].addr, + epCfg.startBuf); + phytium_write8(&priv->regs->ep[num - 1].txcon, 0); + gadgetEp->gadgetEp.maxPacket = epCfg.maxPacketSize; + } else { + if (epCfg.startBuf) + phytium_write16(&priv->regs->rxstaddr[num - 1].addr, + epCfg.startBuf); + phytium_write8(&priv->regs->ep[num - 1].rxcon, 0); + gadgetEp->gadgetEp.maxPacket = epCfg.maxPacketSize; + } + + gadgetEp->gadgetEp.ops = &gadgetEpOps; + gadgetEp->gadgetEp.address = isInEp ? (0x80 | num) : num; + gadgetEp->gadgetEp.maxburst = 0; + gadgetEp->gadgetEp.mult = 0; + gadgetEp->gadgetEp.maxStreams = 0; + priv->endpointInList++; + list_add_tail(&gadgetEp->gadgetEp.epList, &priv->gadgetDev.epList); + } +} + +static void gadgetInitEndpoint(struct GADGET_CTRL *priv) +{ + if (!priv) + return; + + gadgetInitDeviceEp(priv, 1); + gadgetInitDeviceEp(priv, 0); +} + +static void gadgetSetup(struct GADGET_CTRL *priv) +{ + if (!priv) + return; + + INIT_LIST_HEAD(&priv->gadgetDev.epList); + priv->gadgetDev.state = USB_STATE_NOTATTACHED; + priv->gadgetDev.maxSpeed = USB_SPEED_HIGH; + priv->gadgetDev.speed = USB_SPEED_FULL; + snprintf(priv->gadgetDev.name, sizeof(priv->gadgetDev.name), "Phytium USB SD Driver"); + + gadgetInitEndpoint(priv); + + phytium_write8(&priv->regs->ep0maxpack, 0x40); + phytium_write16(&priv->regs->rxien, 0); + phytium_write16(&priv->regs->txien, 0); + phytium_write16(&priv->regs->rxirq, 0xFFFF); + phytium_write16(&priv->regs->txirq, 0xFFFF); + phytium_write8(&priv->regs->usbirq, 0xEF); + phytium_write8(&priv->regs->endprst, ENDPRST_IO_TX); + phytium_write8(&priv->regs->endprst, ENDPRST_FIFORST | ENDPRST_TOGRST | ENDPRST_IO_TX); + phytium_write8(&priv->regs->endprst, ENDPRST_FIFORST | ENDPRST_TOGRST); + priv->isReady = 1; +} +int32_t gadgetInit(struct GADGET_CTRL *priv, struct GADGET_CFG *config, + struct GADGET_CALLBACKS *callbacks, struct device *pdev) +{ + struct DMA_SYSREQ dmaSysReq; + uint8_t usbcs; + + if (!priv || !config || !callbacks) + return -EINVAL; + + priv->dev = pdev; + priv->eventCallback = *callbacks; + priv->gadgetCfg = *config; + priv->regs = (struct HW_REGS *)config->regBase; + priv->phy_regs = (struct VHUB_REGS *)config->phy_regBase; + priv->gadgetDrv = GADGET_GetInstance(); + priv->dmaDrv = DMA_GetInstance(); + priv->dmaController = (void *)(priv + 1); + priv->dmaCfg.dmaModeRx = 0xFFFF; + priv->dmaCfg.dmaModeTx = 0xFFFF; + priv->dmaCfg.regBase = config->regBase + 0x400; + priv->dmaCfg.trbAddr = config->trbAddr; + priv->dmaCfg.trbDmaAddr = config->trbDmaAddr; + + priv->dmaDrv->dma_probe(NULL, &dmaSysReq); + priv->privBuffAddr = (uint8_t *)((uintptr_t)config->trbAddr + dmaSysReq.trbMemSize); + priv->privBuffDma = (uintptr_t)((uintptr_t)config->trbDmaAddr + dmaSysReq.trbMemSize); + priv->dmaCallback.complete = gadget_CallbackTransfer; + priv->dmaDrv->dma_init(priv->dmaController, &priv->dmaCfg, &priv->dmaCallback); + priv->dmaDrv->dma_setParentPriv(priv->dmaController, priv); + + usbcs = phytium_read8(&priv->regs->usbcs); + usbcs |= USBCS_DISCON | USBCS_LPMNYET; + phytium_write8(&priv->regs->usbcs, usbcs); + + gadgetSetup(priv); + + usbcs = phytium_read8(&priv->regs->usbcs); + usbcs &= USBCS_DISCON; + phytium_write8(&priv->regs->usbcs, usbcs); + + return 0; +} + +static void gadgetDestroy(struct GADGET_CTRL *priv) +{ + pr_debug("Destroy Device Controller driver\n"); + + if (priv) + return; + gadgetDisconnect(priv); + + gadgetStopActivity(priv); + + phytium_write8(&priv->regs->usbcs, USBCS_DISCON); + + priv->isReady = 0; +} + +static void gadgetStart(struct GADGET_CTRL *priv) +{ + uint8_t usbien, usbcs; + + pr_debug("Usb Device Controller start\n"); + if (!priv) + return; + + usbien = phytium_read8(&priv->regs->usbien); + usbien |= USBIR_URES | USBIR_SUDAV | USBIR_LPMIR; + phytium_write8(&priv->regs->usbien, usbien); + + usbcs = phytium_read8(&priv->regs->usbcs); + usbcs &= ~USBCS_DISCON; + phytium_write8(&priv->regs->usbcs, usbcs); + + priv->dmaDrv->dma_start(priv->dmaController); +} + +static void gadgetReset(struct GADGET_CTRL *priv) +{ + int i = 0; + + pr_debug("Usb Disable Device Activity\n"); + + if (!priv) + return; + + if (priv->gadgetDev.speed != USB_SPEED_UNKNOWN) + gadgetDisconnect(priv); + + priv->gadgetDev.aHnpSupport = 0; + priv->gadgetDev.bHnpEnable = 0; + priv->gadgetDev.state = USB_STATE_DEFAULT; + priv->deviceAddress = 0; + priv->ep0State = GADGET_EP0_STAGE_SETUP; + + gadgetStopActivity(priv); + + for (i = 0; i < 1000; i++) { + priv->gadgetDev.speed = gadgetGetActualSpeed(priv); + if (priv->gadgetDev.speed == USB_SPEED_HIGH) + return; + } +} + +static void gadgetStop(struct GADGET_CTRL *priv) +{ + pr_debug("Usb Device Controller stop\n"); + + if (!priv) + return; + + if (!priv->isReady) + return; + + gadgetReset(priv); + + phytium_write8(&priv->regs->usbien, 0); + priv->dmaDrv->dma_stop(priv->dmaController); + + priv->isReady = 0; +} + +static void gadgetIsr(struct GADGET_CTRL *priv) +{ + uint8_t usbirq, usbien, usbcs; + + if (!priv) + return; + + usbirq = phytium_read8(&priv->regs->usbirq); + usbien = phytium_read8(&priv->regs->usbien); + + pr_debug("usbirq:0x%x usbien:0x%x\n", usbirq, usbien); + + usbirq = usbirq & usbien; + + if (!usbirq) + goto DMA_IRQ; + + if (usbirq & USBIR_LPMIR) { + pr_debug("USBIRQ LPM\n"); + usbcs = phytium_read8(&priv->regs->usbcs); + usbcs &= ~USBCS_LPMNYET; + phytium_write8(&priv->regs->usbcs, usbcs); + phytium_write8(&priv->regs->usbirq, USBIR_LPMIR); + } + + if (usbirq & USBIR_URES) { + pr_debug("USBIRQ RESET\n"); + phytium_write8(&priv->regs->usbirq, USBIR_URES); + priv->releaseEp0Flag = 1; + gadgetReset(priv); + priv->releaseEp0Flag = 0; + priv->gadgetDev.state = USB_STATE_DEFAULT; + if (priv->eventCallback.connect) + priv->eventCallback.connect(priv); + } + + if (usbirq & USBIR_HSPEED) { + pr_debug("USBIRQ HighSpeed\n"); + phytium_write8(&priv->regs->usbirq, USBIR_HSPEED); + priv->gadgetDev.speed = USB_SPEED_HIGH; + } + + if (usbirq & USBIR_SUDAV) { + pr_debug("USBIRQ SUDAV\n"); + priv->ep0State = GADGET_EP0_STAGE_SETUP; + gadgetEp0Irq(priv); + } + + if (usbirq & USBIR_SOF) { + pr_debug("USBIRQ SOF\n"); + phytium_write8(&priv->regs->usbirq, USBIR_SOF); + } + + if (usbirq & USBIR_SUTOK) { + pr_debug("USBIRQ SUTOK\n"); + phytium_write8(&priv->regs->usbirq, USBIR_SUTOK); + } + + if (usbirq & USBIR_SUSP) { + pr_debug("USBIRQ SUSPEND\n"); + phytium_write8(&priv->regs->usbirq, USBIR_SUSP); + } + + return; +DMA_IRQ: + priv->dmaDrv->dma_isr(priv->dmaController); +} + +static void gadgetGetDevInstance(struct GADGET_CTRL *priv, struct GADGET_DEV **dev) +{ + if (!priv || !dev) + return; + + *dev = &priv->gadgetDev; +} + +static int32_t gadgetGetFrame(struct GADGET_CTRL *priv, uint32_t *numOfFrame) +{ + if (!priv || !numOfFrame) + return -EINVAL; + + *numOfFrame = phytium_read16(&priv->regs->frmnr); + + return 0; +} + +static int32_t gadgetWakeUp(struct GADGET_CTRL *priv) +{ + if (!priv) + return -EINVAL; + + return -ENOTSUPP; +} + +static int32_t gadgetSetSelfPowered(struct GADGET_CTRL *priv) +{ + if (!priv) + return -EINVAL; + + priv->isSelfPowered = 1; + + return 0; +} + +static int32_t gadgetClearSelfPowered(struct GADGET_CTRL *priv) +{ + if (!priv) + return -EINVAL; + + priv->isSelfPowered = 0; + + return 0; +} + +static int32_t gadgetVbusSession(struct GADGET_CTRL *priv, uint8_t isActive) +{ + if (!priv) + return -EINVAL; + + return -ENOTSUPP; +} + +static int32_t gadgetVbusDraw(struct GADGET_CTRL *priv, uint8_t mA) +{ + if (!priv) + return -EINVAL; + + return -ENOTSUPP; +} + +static int32_t gadgetPullUp(struct GADGET_CTRL *priv, uint8_t isOn) +{ + if (!priv) + return -EINVAL; + + return -ENOTSUPP; +} + +struct GADGET_OBJ GadgetObj = { + .gadget_init = gadgetInit, + .gadget_destroy = gadgetDestroy, + .gadget_start = gadgetStart, + .gadget_stop = gadgetStop, + .gadget_isr = gadgetIsr, + //endpoint operation + .gadget_epEnable = gadgetEpEnable, + .gadget_epDisable = gadgetEpDisable, + .gadget_epSetHalt = gadgetEpSetHalt, + .gadget_epSetWedge = gadgetEpSetWedge, + .gadget_epFifoStatus = gadgetEpFifoStatus, + .gadget_epFifoFlush = gadgetEpFifoFlush, + .gadget_reqQueue = gadgetEpQueue, + .gadget_reqDequeue = gadgetEpDequeue, + .gadget_reqAlloc = gadgetEpAllocRequest, + .gadget_reqFree = gadgetEpFreeRequest, + + //Device operations + .gadget_getDevInstance = gadgetGetDevInstance, + .gadget_dGetFrame = gadgetGetFrame, + .gadget_dWakeUp = gadgetWakeUp, + .gadget_dSetSelfpowered = gadgetSetSelfPowered, + .gadget_dClearSelfpowered = gadgetClearSelfPowered, + .gadget_dVbusSession = gadgetVbusSession, + .gadget_dVbusDraw = gadgetVbusDraw, + .gadget_dPullUp = gadgetPullUp, +}; + +struct GADGET_OBJ *GADGET_GetInstance(void) +{ + return &GadgetObj; +} + +static int phytium_gadget_set_default_cfg(struct phytium_cusb *config) +{ + int index; + + config->gadget_cfg.regBase = (uintptr_t)config->regs; + config->gadget_cfg.phy_regBase = (uintptr_t)config->phy_regs; + config->gadget_cfg.dmaInterfaceWidth = GADGET_DMA_32_WIDTH; + + for (index = 0; index < 16; index++) { + if (index == 0) { + config->gadget_cfg.epIN[index].bufferingValue = 1; + config->gadget_cfg.epIN[index].maxPacketSize = 64; + config->gadget_cfg.epIN[index].startBuf = 0; + + config->gadget_cfg.epOUT[index].bufferingValue = 1; + config->gadget_cfg.epOUT[index].maxPacketSize = 64; + config->gadget_cfg.epOUT[index].startBuf = 0; + } else { + config->gadget_cfg.epIN[index].bufferingValue = 4; + config->gadget_cfg.epIN[index].maxPacketSize = 1024; + config->gadget_cfg.epIN[index].startBuf = 64 + 4096 * (index - 1); + + config->gadget_cfg.epOUT[index].bufferingValue = 4; + config->gadget_cfg.epOUT[index].maxPacketSize = 1024; + config->gadget_cfg.epOUT[index].startBuf = 64 + 4096 * (index - 1); + } + } + + return 0; +} + +void gadget_callback_connect(struct GADGET_CTRL *priv) +{ + if (!priv) + return; +} + +void gadget_callback_disconnect(struct GADGET_CTRL *priv) +{ + if (!priv) + return; +} + +int32_t gadget_callback_setup(struct GADGET_CTRL *priv, struct usb_ctrlrequest *ctrl) +{ + struct phytium_cusb *config; + int ret = 0; + + if (!priv || !ctrl) + return -EINVAL; + + config = dev_get_drvdata(priv->dev); + if (!config) + return -1; + + if (!config->gadget_driver) + return -EOPNOTSUPP; + + if (ctrl->bRequestType & USB_DIR_IN) + config->ep0_data_stage_is_tx = 1; + else + config->ep0_data_stage_is_tx = 0; + + spin_unlock(&config->lock); + ret = config->gadget_driver->setup(&config->gadget, ctrl); + spin_lock(&config->lock); + + if (ret == 0x7FFF) + return ret; + + if (ret < 0) + return 1; + + return 0; +} + +void *gadget_callback_usbRequestMemAlloc(struct GADGET_CTRL *priv, u32 size) +{ + struct GADGET_REQ *gadget_req = NULL; + + gadget_req = kzalloc(size, GFP_NOWAIT); + if (!gadget_req) + return NULL; + + return gadget_req; +} + +void gadget_callback_usbRequestMemFree(struct GADGET_CTRL *priv, void *usbReq) +{ + if (!usbReq) + return; + + kfree(usbReq); +} + +static void init_peripheral_ep(struct phytium_cusb *config, + struct phytium_ep *phy_ep, struct GADGET_EP *gadget_ep, int is_tx) +{ + if (!config || !phy_ep || !gadget_ep) + return; + + memset(phy_ep, 0, sizeof(*phy_ep)); + phy_ep->config = config; + phy_ep->is_tx = is_tx; + phy_ep->gadget_ep = gadget_ep; + phy_ep->ep_num = gadget_ep->address & 0xF; + phy_ep->end_point.maxpacket = gadget_ep->maxPacket; + phy_ep->end_point.maxpacket_limit = 1024; + + INIT_LIST_HEAD(&phy_ep->req_list); + sprintf(phy_ep->name, "ep%d%s", phy_ep->ep_num, is_tx ? "in" : "out"); + + switch (phy_ep->ep_num) { + case 0: + phy_ep->end_point.caps.type_control = 1; + break; + case 1: + phy_ep->end_point.caps.type_bulk = 1; + break; + case 2: + phy_ep->end_point.caps.type_int = 1; + break; + case 3: + phy_ep->end_point.caps.type_iso = 1; + break; + default: + phy_ep->end_point.caps.type_int = 1; + phy_ep->end_point.caps.type_bulk = 1; + break; + } + + if (is_tx) { + phy_ep->end_point.caps.dir_in = 1; + phy_ep->end_point.caps.dir_out = 0; + } else { + phy_ep->end_point.caps.dir_in = 0; + phy_ep->end_point.caps.dir_out = 1; + } + + phy_ep->end_point.name = phy_ep->name; + + INIT_LIST_HEAD(&phy_ep->end_point.ep_list); + + if (!phy_ep->ep_num) { + phy_ep->end_point.ops = &gadget_ep0_ops; + config->gadget.ep0 = &phy_ep->end_point; + + if (config->gadget_dev->maxSpeed > USB_SPEED_HIGH) + config->gadget.ep0->maxpacket = 9; + } else { + phy_ep->end_point.ops = &gadget_ep_ops; + list_add_tail(&phy_ep->end_point.ep_list, &config->gadget.ep_list); + } +} + +static void gadget_init_endpoint(struct phytium_cusb *config) +{ + struct list_head *list; + struct GADGET_EP *gadget_ep; + + if (!config) + return; + + INIT_LIST_HEAD(&(config->gadget.ep_list)); + + init_peripheral_ep(config, &config->endpoints_tx[0], config->gadget_dev->ep0, 1); + init_peripheral_ep(config, &config->endpoints_rx[0], config->gadget_dev->ep0, 0); + + list_for_each(list, &config->gadget_dev->epList) { + gadget_ep = (struct GADGET_EP *)list; + if (gadget_ep->address & USB_DIR_IN) + init_peripheral_ep(config, &config->endpoints_tx[gadget_ep->address & 0xf], + gadget_ep, 1); + else + init_peripheral_ep(config, &config->endpoints_rx[gadget_ep->address & 0xf], + gadget_ep, 0); + } +} + +static int gadget_setup(struct phytium_cusb *config) +{ + int ret = -1; + + config->gadget_obj->gadget_getDevInstance(config->gadget_priv, &config->gadget_dev); + config->gadget.ops = &phytium_gadget_ops; + config->gadget.max_speed = config->gadget_dev->maxSpeed; + config->gadget.speed = USB_SPEED_HIGH; + config->gadget.name = "phytium_gadget"; + config->gadget.is_otg = 0; + + gadget_init_endpoint(config); + + ret = usb_add_gadget_udc(config->dev, &config->gadget); + if (ret) + goto err; + + return 0; + +err: + config->gadget.dev.parent = NULL; + device_unregister(&config->gadget.dev); + return ret; +} + +int phytium_gadget_reinit(struct phytium_cusb *config) +{ + struct GADGET_CTRL *ctrl; + + if (!config) + return 0; + + ctrl = (struct GADGET_CTRL *)config->gadget_priv; + if (!ctrl) + return 0; + + gadgetStop(ctrl); + + config->gadget_obj->gadget_init(config->gadget_priv, &config->gadget_cfg, + &config->gadget_callbacks, config->dev); + + return 0; +} + +int phytium_gadget_init(struct phytium_cusb *config) +{ + int ret; + + if (!config) + return 0; + + phytium_gadget_set_default_cfg(config); + config->gadget_obj = &GadgetObj; + + config->dma_cfg.regBase = config->gadget_cfg.regBase + 0x400; + config->dma_obj = DMA_GetInstance(); + config->dma_obj->dma_probe(&config->dma_cfg, &config->dma_sysreq); + + config->gadget_sysreq.privDataSize = sizeof(struct GADGET_CTRL); + config->gadget_sysreq.trbMemSize = config->dma_sysreq.trbMemSize + GADGET_PRIV_BUFFER_SIZE; + config->gadget_sysreq.privDataSize += config->dma_sysreq.privDataSize; + + config->gadget_priv = devm_kzalloc(config->dev, + config->gadget_sysreq.privDataSize, GFP_KERNEL); + if (!config->gadget_priv) { + ret = -ENOMEM; + goto err_probe; + } + config->gadget_cfg.trbAddr = dma_alloc_coherent(config->dev, + config->gadget_sysreq.trbMemSize, + (dma_addr_t *)&config->gadget_cfg.trbDmaAddr, GFP_KERNEL); + if (!config->gadget_cfg.trbAddr) { + ret = -ENOMEM; + goto err_dma_coherent; + } + + config->gadget_callbacks.connect = gadget_callback_connect; + config->gadget_callbacks.disconnect = gadget_callback_disconnect; + config->gadget_callbacks.setup = gadget_callback_setup; + config->gadget_callbacks.usbRequestMemAlloc = gadget_callback_usbRequestMemAlloc; + config->gadget_callbacks.usbRequestMemFree = gadget_callback_usbRequestMemFree; + + ret = config->gadget_obj->gadget_init(config->gadget_priv, &config->gadget_cfg, + &config->gadget_callbacks, config->dev); + if (ret) { + ret = -ENODEV; + goto err_init; + } + + //dev_set_drvdata(config->dev, config); + + gadget_setup(config); + + return 0; + +err_init: + dma_free_coherent(config->dev, config->gadget_sysreq.trbMemSize, + config->gadget_cfg.trbAddr, config->gadget_cfg.trbDmaAddr); +err_dma_coherent: +err_probe: + dev_set_drvdata(config->dev, NULL); + + return ret; +} + +int phytium_gadget_uninit(struct phytium_cusb *config) +{ + if (config) + usb_del_gadget_udc(&config->gadget); + + return 0; +} + +#ifdef CONFIG_PM +int phytium_gadget_resume(void *priv) +{ + struct GADGET_CTRL *ctrl; + uint32_t gen_cfg; + unsigned long flags = 0; + struct phytium_cusb *config = (struct phytium_cusb *)priv; + + if (!config) + return 0; + + ctrl = (struct GADGET_CTRL *)config->gadget_priv; + if (!ctrl) + return 0; + + spin_lock_irqsave(&config->lock, flags); + phytium_gadget_reinit(config); + + if (config->gadget_driver) { + config->gadget_obj->gadget_start(config->gadget_priv); + if (ctrl->phy_regs) { + gen_cfg = phytium_read32(&ctrl->phy_regs->gen_cfg); + gen_cfg = gen_cfg & (~BIT(7)); + phytium_write32(&ctrl->phy_regs->gen_cfg, gen_cfg); + } + } + spin_unlock_irqrestore(&config->lock, flags); + + return 0; +} + +int phytium_gadget_suspend(void *priv) +{ + return 0; +} +#endif diff --git a/target/linux/phytium/files-5.10/drivers/usb/phytium/gadget.h b/target/linux/phytium/files-5.10/drivers/usb/phytium/gadget.h new file mode 100644 index 000000000..d87b55ade --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/usb/phytium/gadget.h @@ -0,0 +1,253 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __PHYTIUM_GADGET_H_ +#define __PHYTIUM_GADGET_H_ + +#include +#include +#include "dma.h" + +struct GADGET_CTRL; +struct GADGET_EP; +struct GADGET_REQ; + +enum GADGET_EP_STATE { + GADGET_EP_FREE, + GADGET_EP_ALLOCATED, + GADGET_EP_BUSY, + GADGET_EP_NOT_IMPLEMENTED +}; + +enum GADGET_EP0_STAGE { + GADGET_EP0_STAGE_SETUP, + GADGET_EP0_STAGE_IN, + GADGET_EP0_STAGE_OUT, + GADGET_EP0_STAGE_STATUSIN, + GADGET_EP0_STAGE_STATUSOUT, + GADGET_EP0_STAGE_ACK +}; + +struct GADGET_EP_OPS { + int32_t (*epEnable)(struct GADGET_CTRL *priv, struct GADGET_EP *ep, + const struct usb_endpoint_descriptor *desc); + + int32_t (*epDisable)(struct GADGET_CTRL *priv, struct GADGET_EP *ep); + + int32_t (*epSetHalt)(struct GADGET_CTRL *priv, struct GADGET_EP *ep, uint8_t value); + + int32_t (*epSetWedge)(struct GADGET_CTRL *priv, struct GADGET_EP *ep); + + int32_t (*epFifoStatus)(struct GADGET_CTRL *priv, struct GADGET_EP *ep); + + int32_t (*epFifoFlush)(struct GADGET_CTRL *priv, struct GADGET_EP *ep); + + int32_t (*reqQueue)(struct GADGET_CTRL *priv, struct GADGET_EP *ep, + struct GADGET_REQ *req); + + int32_t (*reqDequeue)(struct GADGET_CTRL *priv, struct GADGET_EP *ep, + struct GADGET_REQ *req); + + int32_t (*reqAlloc)(struct GADGET_CTRL *priv, struct GADGET_EP *ep, + struct GADGET_REQ **req); + + void (*reqFree)(struct GADGET_CTRL *priv, struct GADGET_EP *ep, + struct GADGET_REQ *req); +}; + +struct GADGET_EP { + struct list_head epList; + char name[255]; + struct GADGET_EP_OPS *ops; + uint16_t maxPacket; + uint16_t maxStreams; + uint8_t mult; + uint8_t maxburst; + uint8_t address; + const struct usb_endpoint_descriptor *desc; + const struct usb_ss_ep_comp_descriptor *compDesc; +}; + +enum GADGET_DMAInterfaceWidth { + GADGET_DMA_32_WIDTH = 4, + GADGET_DMA_64_WIDTH = 8, +}; + +struct GADGET_EP_CFG { + uint8_t bufferingValue; + uint16_t startBuf; + uint16_t maxPacketSize; +}; + +struct GADGET_CFG { + uintptr_t regBase; + uintptr_t phy_regBase; + struct GADGET_EP_CFG epIN[16]; + struct GADGET_EP_CFG epOUT[16]; + enum GADGET_DMAInterfaceWidth dmaInterfaceWidth; + void *trbAddr; + uintptr_t trbDmaAddr; +}; + +struct GADGET_SYSREQ { + uint32_t privDataSize; + uint32_t trbMemSize; +}; + +struct GadgetEp { + struct GADGET_EP gadgetEp; + enum GADGET_EP_STATE state; + uint8_t hwEpNum; + uint8_t isInEp; + struct list_head request; + uint8_t iso_flag; + void *channel; + uint32_t requestsInList; + uint8_t wedged; +}; + +struct GADGET_DEV { + struct list_head epList; + struct GADGET_EP *ep0; + unsigned int speed; + unsigned int maxSpeed; + enum usb_device_state state; + uint8_t sgSupported; + uint8_t bHnpEnable; + uint8_t aHnpSupport; + char name[255]; +}; + +struct GADGET_SgList { + uintptr_t link; + uint32_t offset; + uint32_t length; + uintptr_t dmaAddress; +}; + +struct GADGET_REQ { + struct list_head list; + void *buf; + uint32_t length; + uintptr_t dma; + uint32_t numOfSgs; + uint32_t numMappedSgs; + uint16_t streamId; + uint8_t oInterrupt; + uint8_t zero; + uint8_t shortNotOk; + void *context; + uint32_t status; + uint32_t actual; + struct GADGET_SgList *sg; + void (*complete)(struct GADGET_EP *ep, struct GADGET_REQ *req); +}; + + +struct GADGET_CALLBACKS { + void (*disconnect)(struct GADGET_CTRL *priv); + + void (*connect)(struct GADGET_CTRL *priv); + + int32_t (*setup)(struct GADGET_CTRL *priv, + struct usb_ctrlrequest *ctrl); + + void *(*usbRequestMemAlloc)(struct GADGET_CTRL *priv, + uint32_t requiredSize); + + void (*usbRequestMemFree)(struct GADGET_CTRL *priv, void *usbRequest); +}; + +struct GADGET_OBJ { + int32_t (*gadget_init)(struct GADGET_CTRL *priv, struct GADGET_CFG *config, + struct GADGET_CALLBACKS *callbacks, struct device *pdev); + + void (*gadget_destroy)(struct GADGET_CTRL *priv); + + void (*gadget_start)(struct GADGET_CTRL *priv); + + void (*gadget_stop)(struct GADGET_CTRL *priv); + + void (*gadget_isr)(struct GADGET_CTRL *priv); + + int32_t (*gadget_epEnable)(struct GADGET_CTRL *priv, struct GADGET_EP *ep, + const struct usb_endpoint_descriptor *desc); + + int32_t (*gadget_epDisable)(struct GADGET_CTRL *priv, struct GADGET_EP *ep); + + int32_t (*gadget_epSetHalt)(struct GADGET_CTRL *priv, struct GADGET_EP *ep, uint8_t value); + + int32_t (*gadget_epSetWedge)(struct GADGET_CTRL *priv, struct GADGET_EP *ep); + + int32_t (*gadget_epFifoStatus)(struct GADGET_CTRL *priv, struct GADGET_EP *ep); + + void (*gadget_epFifoFlush)(struct GADGET_CTRL *priv, struct GADGET_EP *ep); + + int32_t (*gadget_reqQueue)(struct GADGET_CTRL *priv, struct GADGET_EP *ep, + struct GADGET_REQ *req); + + int32_t (*gadget_reqDequeue)(struct GADGET_CTRL *priv, struct GADGET_EP *ep, + struct GADGET_REQ *req); + + int32_t (*gadget_reqAlloc)(struct GADGET_CTRL *priv, struct GADGET_EP *ep, + struct GADGET_REQ **req); + + void (*gadget_reqFree)(struct GADGET_CTRL *priv, struct GADGET_EP *ep, + struct GADGET_REQ *req); + + void (*gadget_getDevInstance)(struct GADGET_CTRL *priv, struct GADGET_DEV **dev); + + int32_t (*gadget_dGetFrame)(struct GADGET_CTRL *priv, uint32_t *numOfFrame); + + int32_t (*gadget_dWakeUp)(struct GADGET_CTRL *priv); + + int32_t (*gadget_dSetSelfpowered)(struct GADGET_CTRL *priv); + + int32_t (*gadget_dClearSelfpowered)(struct GADGET_CTRL *priv); + + int32_t (*gadget_dVbusSession)(struct GADGET_CTRL *priv, uint8_t isActive); + + int32_t (*gadget_dVbusDraw)(struct GADGET_CTRL *priv, uint8_t mA); + + int32_t (*gadget_dPullUp)(struct GADGET_CTRL *priv, uint8_t isOn); + + void (*gadget_dGetConfigParams)(struct GADGET_CTRL *priv, + struct usb_dcd_config_params *configParams); +}; + +struct GADGET_CTRL { + struct device *dev; + struct GADGET_DEV gadgetDev; + struct HW_REGS *regs; + struct GADGET_OBJ *gadgetDrv; + struct GADGET_CFG gadgetCfg; + struct GADGET_CALLBACKS eventCallback; + struct GadgetEp in[16]; + struct GadgetEp out[16]; + enum GADGET_EP0_STAGE ep0State; + uint8_t isRemoteWakeup; + uint8_t isSelfPowered; + uint8_t deviceAddress; + struct DMA_OBJ *dmaDrv; + void *dmaController; + struct DMA_CFG dmaCfg; + struct DMA_CALLBACKS dmaCallback; + uint8_t releaseEp0Flag; + uint8_t isReady; + uint8_t *privBuffAddr; + uintptr_t privBuffDma; + uint8_t endpointInList; + uint8_t hostRequestFlag; + struct VHUB_REGS *phy_regs; +}; + +struct GadgetRequest { + struct GADGET_REQ request; + struct GadgetEp *ep; + struct GADGET_DEV *dev; + uint8_t zlp; +}; + +struct GADGET_OBJ *GADGET_GetInstance(void); + +#endif /* __LINUX_PHYTIUM_GADGET */ + diff --git a/target/linux/phytium/files-5.10/drivers/usb/phytium/host.c b/target/linux/phytium/files-5.10/drivers/usb/phytium/host.c new file mode 100644 index 000000000..00d972487 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/usb/phytium/host.c @@ -0,0 +1,2671 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include +#include +//#include "list.h" +#include "core.h" +#include "dma.h" +#include "hw-regs.h" + +#define DRV_NAME "phytium_usb" + +#define HOST_GENERIC_EP_CONTROLL 0x00 +#define HOST_GENERIC_EP_ISOC 0x01 +#define HOST_GENERIC_EP_BULK 0x02 +#define HOST_GENERIC_EP_INT 0x03 + +#define HOST_ESTALL 1 +#define HOST_EUNHANDLED 2 +#define HOST_EAUTOACK 3 +#define HOST_ESHUTDOWN 4 + +#define HOST_EP_NUM 16 + +static inline struct HOST_REQ *getUsbRequestEntry(struct list_head *list) +{ + return (struct HOST_REQ *)((uintptr_t)list - (uintptr_t)&(((struct HOST_REQ *)0)->list)); +} + +static inline struct HOST_EP_PRIV *getUsbHEpPrivEntry(struct list_head *list) +{ + struct HOST_EP_PRIV *hostEpPriv; + + if (list_empty(list)) + return NULL; + + hostEpPriv = (struct HOST_EP_PRIV *)((uintptr_t)list - + (uintptr_t)&(((struct HOST_EP_PRIV *)0)->node)); + + return hostEpPriv; +} + +static struct HOST_REQ *getNextReq(struct HOST_EP *usbEp) +{ + struct list_head *queue; + + if (!usbEp) + return NULL; + + queue = &usbEp->reqList; + + if (list_empty(queue)) + return NULL; + + return getUsbRequestEntry(queue->next); +} + +static void host_SetVbus(struct HOST_CTRL *priv, uint8_t isOn) +{ + uint8_t otgctrl = phytium_read8(&priv->regs->otgctrl); + + if (isOn) { + if (!(otgctrl & OTGCTRL_BUSREQ) || (otgctrl & OTGCTRL_ABUSDROP)) { + otgctrl &= ~OTGCTRL_ABUSDROP; + otgctrl |= OTGCTRL_BUSREQ; + phytium_write8(&priv->regs->otgctrl, otgctrl); + } + priv->otgState = HOST_OTG_STATE_A_WAIT_BCON; + } else { + if ((otgctrl & OTGCTRL_BUSREQ) || (otgctrl & OTGCTRL_ABUSDROP)) { + otgctrl |= OTGCTRL_ABUSDROP; + otgctrl &= ~OTGCTRL_BUSREQ; + phytium_write8(&priv->regs->otgctrl, otgctrl); + } + priv->otgState = HOST_OTG_STATE_A_IDLE; + } +} + +static inline void disconnectHostDetect(struct HOST_CTRL *priv) +{ + uint8_t otgctrl, otgstate; + uint32_t gen_cfg; + + if (!priv) + return; + + otgctrl = phytium_read8(&priv->regs->otgctrl); + if ((otgctrl & OTGCTRL_ASETBHNPEN) && priv->otgState == HOST_OTG_STATE_A_SUSPEND) + pr_info("Device no Response\n"); + + phytium_write8(&priv->regs->otgirq, OTGIRQ_CONIRQ); +retry: + otgstate = phytium_read8(&priv->regs->otgstate); + if ((otgstate == HOST_OTG_STATE_A_HOST || otgstate == HOST_OTG_STATE_B_HOST)) { + pr_info("IRQ OTG: DisconnIrq Babble\n"); + goto retry; + } + + phytium_write8(&priv->regs->endprst, ENDPRST_IO_TX); + phytium_write8(&priv->regs->endprst, ENDPRST_FIFORST | ENDPRST_TOGRST | ENDPRST_IO_TX); + phytium_write8(&priv->regs->endprst, ENDPRST_FIFORST | ENDPRST_TOGRST); + phytium_write8(&priv->regs->ep0fifoctrl, FIFOCTRL_FIFOAUTO | 0 | 0x04); + phytium_write8(&priv->regs->ep0fifoctrl, FIFOCTRL_FIFOAUTO | FIFOCTRL_IO_TX | 0 | 0x04); + + priv->portStatus = USB_PORT_STAT_POWER; + priv->portStatus |= USB_PORT_STAT_C_CONNECTION << 16; + + if (priv->hostCallbacks.portStatusChange) + priv->hostCallbacks.portStatusChange(priv); + + if (priv->otgState == HOST_OTG_STATE_A_SUSPEND) + host_SetVbus(priv, 1); + + priv->otgState = HOST_OTG_STATE_A_IDLE; + if (priv->custom_regs) { + phytium_write32(&priv->custom_regs->wakeup, 1); + } else { + gen_cfg = phytium_read32(&priv->vhub_regs->gen_cfg); + gen_cfg |= BIT(1); + phytium_write32(&priv->vhub_regs->gen_cfg, gen_cfg); + } +} + +static inline void A_IdleDetect(struct HOST_CTRL *priv, uint8_t otgstate) +{ + uint8_t otgctrl; + + if (!priv) + return; + + phytium_write8(&priv->regs->otgirq, OTGIRQ_IDLEIRQ); + + if (otgstate != HOST_OTG_STATE_A_IDLE) { + pr_info("IRQ OTG: A_IDLE Babble\n"); + return; + } + + priv->portStatus = 0; + otgctrl = phytium_read8(&priv->regs->otgctrl); + otgctrl &= ~OTGCTRL_ASETBHNPEN; + phytium_write8(&priv->regs->otgctrl, otgctrl); + + host_SetVbus(priv, 1); + + priv->otgState = HOST_OTG_STATE_A_IDLE; +} + +static inline void B_IdleDetect(struct HOST_CTRL *priv, uint8_t otgstate) +{ + uint8_t otgctrl, usbcs; + + if (!priv) + return; + + phytium_write8(&priv->regs->otgirq, OTGIRQ_IDLEIRQ); + + if (otgstate != HOST_OTG_STATE_B_IDLE) { + pr_info("IRQ OTG: B_IDLE Babble\n"); + return; + } + + otgctrl = phytium_read8(&priv->regs->otgctrl); + otgctrl &= ~OTGCTRL_ASETBHNPEN; + phytium_write8(&priv->regs->otgctrl, otgctrl); + + host_SetVbus(priv, 0); + + priv->otgState = HOST_OTG_STATE_B_IDLE; + + usbcs = phytium_read8(&priv->regs->usbcs); + usbcs &= ~USBCS_DISCON; + phytium_write8(&priv->regs->usbcs, usbcs); +} + +static uint32_t waitForBusyBit(struct HOST_CTRL *priv, struct HostEp *hwEp) +{ + uint8_t *csReg; + uint8_t flag = CS_BUSY; + uint8_t buf = 0; + uint8_t val = CS_BUSY; + uint8_t otgstate; + uint8_t bufflag = 0; + + if (!priv || !hwEp) + return 0; + + if (hwEp->isInEp) + return 0; + + if (hwEp->hwEpNum == 0) { + csReg = &priv->regs->ep0cs; + flag = EP0CS_TXBUSY_MASK; + buf = 0; + } else { + csReg = &priv->regs->ep[hwEp->hwEpNum - 1].txcs; + buf = phytium_read8(&priv->regs->ep[hwEp->hwEpNum - 1].txcon) & CON_BUF; + } + + while ((val & flag) || bufflag == 0) { + otgstate = phytium_read8(&priv->regs->otgstate); + if (otgstate != HOST_OTG_STATE_B_HOST && otgstate != HOST_OTG_STATE_A_HOST) { + priv->ep0State = HOST_EP0_STAGE_IDLE; + return HOST_ESHUTDOWN; + } + + val = phytium_read8(csReg); + if (((val & CS_NPAK) >> CS_NPAK_OFFSET) == buf || buf == 0) + bufflag = 1; + else + bufflag = 0; + } + + return 0; +} + +static inline void connectHostDetect(struct HOST_CTRL *priv, uint8_t otgState) +{ + uint32_t gen_cfg; + + if (!priv) + return; + pr_debug("otgState:0x%x pirv->otgState:0x%x\n", otgState, priv->otgState); + if (priv->custom_regs) { + phytium_write32(&priv->custom_regs->wakeup, 0); + } else { + gen_cfg = phytium_read32(&priv->vhub_regs->gen_cfg); + gen_cfg &= ~BIT(1); + phytium_write32(&priv->vhub_regs->gen_cfg, gen_cfg); + } + + phytium_write8(&priv->regs->otgirq, OTGIRQ_CONIRQ); + + if ((otgState != HOST_OTG_STATE_A_HOST) && (otgState != HOST_OTG_STATE_B_HOST)) + return; + + if ((priv->otgState == HOST_OTG_STATE_A_PERIPHERAL) + || (priv->otgState == HOST_OTG_STATE_B_PERIPHERAL)) + priv->otgState = otgState; + + priv->ep0State = HOST_EP0_STAGE_IDLE; + + priv->portStatus &= ~(USB_PORT_STAT_LOW_SPEED | USB_PORT_STAT_HIGH_SPEED | + USB_PORT_STAT_ENABLE); + + priv->portStatus |= USB_PORT_STAT_C_CONNECTION | (USB_PORT_STAT_C_CONNECTION << 16); + priv->dmaDrv->dma_controllerReset(priv->dmaController); + priv->port_resetting = 1; + host_SetVbus(priv, 1); + + switch (phytium_read8(&priv->regs->speedctrl)) { + case SPEEDCTRL_HS: + priv->portStatus |= USB_PORT_STAT_HIGH_SPEED; + pr_debug("detect High speed device\n"); + break; + case SPEEDCTRL_FS: + priv->portStatus &= ~(USB_PORT_STAT_HIGH_SPEED | USB_PORT_STAT_LOW_SPEED); + pr_debug("detect Full speed device\n"); + break; + case SPEEDCTRL_LS: + priv->portStatus |= USB_PORT_STAT_LOW_SPEED; + pr_debug("detect Low speed device\n"); + break; + } + + priv->vBusErrCnt = 0; + priv->dmaDrv->dma_setHostMode(priv->dmaController); + + if (priv->hostCallbacks.portStatusChange) + priv->hostCallbacks.portStatusChange(priv); + + priv->otgState = otgState; +} + +static void hostOtgIrq(struct HOST_CTRL *priv) +{ + uint8_t otgirq, otgien; + uint8_t otgstatus, otgstate; + uint8_t otgctrl; + + if (!priv) + return; + + otgirq = phytium_read8(&priv->regs->otgirq); + otgien = phytium_read8(&priv->regs->otgien); + otgstatus = phytium_read8(&priv->regs->otgstatus); + otgstate = phytium_read8(&priv->regs->otgstate); + otgirq &= otgien; + + if (!otgirq) + return; + + if (otgirq & OTGIRQ_BSE0SRPIRQ) { + otgirq &= ~OTGIRQ_BSE0SRPIRQ; + phytium_write8(&priv->regs->otgirq, OTGIRQ_BSE0SRPIRQ); + + otgctrl = phytium_read8(&priv->regs->otgctrl); + otgctrl &= ~OTGIRQ_BSE0SRPIRQ; + phytium_write8(&priv->regs->otgctrl, otgctrl); + } + + if (otgirq & OTGIRQ_SRPDETIRQ) { + otgirq &= ~OTGIRQ_SRPDETIRQ; + phytium_write8(&priv->regs->otgirq, OTGIRQ_SRPDETIRQ); + + otgctrl = phytium_read8(&priv->regs->otgctrl); + otgctrl &= ~OTGIRQ_SRPDETIRQ; + phytium_write8(&priv->regs->otgctrl, otgctrl); + } + + if (otgirq & OTGIRQ_VBUSERRIRQ) { + otgirq &= ~OTGIRQ_VBUSERRIRQ; + phytium_write8(&priv->regs->otgirq, OTGIRQ_VBUSERRIRQ); + + if (otgstate != HOST_OTG_STATE_A_VBUS_ERR) { + pr_info("IRQ OTG: VBUS ERROR Babble\n"); + return; + } + + host_SetVbus(priv, 0); + priv->otgState = HOST_OTG_STATE_A_VBUS_ERR; + if (priv->portStatus & USB_PORT_STAT_CONNECTION) { + priv->portStatus = USB_PORT_STAT_POWER | (USB_PORT_STAT_C_CONNECTION << 16); + if (priv->hostCallbacks.portStatusChange) + priv->hostCallbacks.portStatusChange(priv); + return; + } + + if (priv->vBusErrCnt >= 3) { + priv->vBusErrCnt = 0; + pr_info("%s %d VBUS OVER CURRENT\n", __func__, __LINE__); + priv->portStatus |= USB_PORT_STAT_OVERCURRENT | + (USB_PORT_STAT_C_OVERCURRENT << 16); + + phytium_write8(&priv->regs->otgirq, OTGIRQ_IDLEIRQ); + } else { + priv->vBusErrCnt++; + host_SetVbus(priv, 1); + phytium_write8(&priv->regs->otgirq, OTGIRQ_IDLEIRQ); + } + } + + if (otgirq & OTGIRQ_CONIRQ) { + if (priv->otgState == HOST_OTG_STATE_A_HOST || + priv->otgState == HOST_OTG_STATE_B_HOST || + priv->otgState == HOST_OTG_STATE_A_SUSPEND) { + if (otgstate == HOST_OTG_STATE_A_WAIT_VFALL || + otgstate == HOST_OTG_STATE_A_WAIT_BCON || + otgstate == HOST_OTG_STATE_A_SUSPEND) + disconnectHostDetect(priv); + } else if (priv->otgState != HOST_OTG_STATE_A_HOST && + priv->otgState != HOST_OTG_STATE_B_HOST && + priv->otgState != HOST_OTG_STATE_A_SUSPEND) + connectHostDetect(priv, otgstate); + + phytium_write8(&priv->regs->otgirq, OTGIRQ_CONIRQ); + } + + if (otgirq & OTGIRQ_IDLEIRQ) { + if (!(otgstatus & OTGSTATUS_ID)) + A_IdleDetect(priv, otgstate); + else + B_IdleDetect(priv, otgstate); + } + + phytium_write8(&priv->regs->otgirq, OTGIRQ_IDCHANGEIRQ | + OTGIRQ_SRPDETIRQ); +} + +static void hostErrorIrq(struct HOST_CTRL *priv) +{ + uint16_t txerrirq, txerrien; + uint16_t rxerrirq, rxerrien; + uint16_t i, mask; + + if (!priv) + return; + + txerrirq = phytium_read16(&priv->regs->txerrirq); + txerrien = phytium_read16(&priv->regs->txerrien); + txerrirq &= txerrien; + + rxerrirq = phytium_read16(&priv->regs->rxerrirq); + rxerrien = phytium_read16(&priv->regs->rxerrien); + rxerrirq &= rxerrirq; + if (!txerrirq && !rxerrirq) + return; + + for (i = 0; i < HOST_EP_NUM; i++) { + mask = 1 << i; + if (rxerrirq & mask) { + phytium_write16(&priv->regs->rxerrirq, mask); + rxerrien &= ~mask; + phytium_write16(&priv->regs->rxerrien, rxerrien); + priv->dmaDrv->dma_errIsr(priv->dmaController, i, 0); + } + + if (txerrirq & mask) { + phytium_write16(&priv->regs->txerrirq, mask); + txerrien &= ~mask; + phytium_write16(&priv->regs->txerrien, txerrien); + priv->dmaDrv->dma_errIsr(priv->dmaController, i, 1); + } + } +} + +static uint32_t decodeErrorCode(uint8_t code) +{ + uint32_t status = 0; + + switch (code) { + case ERR_NONE: + status = 0; + break; + case ERR_CRC: + pr_info("CRC Error\n"); + status = HOST_ESHUTDOWN; + break; + case ERR_DATA_TOGGLE_MISMATCH: + pr_info("Toggle MisMatch Error\n"); + status = HOST_ESHUTDOWN; + break; + case ERR_STALL: + pr_debug("Stall Error\n"); + status = HOST_ESTALL; + break; + case ERR_TIMEOUT: + pr_debug("Timeout Error\n"); + status = HOST_ESHUTDOWN; + break; + case ERR_PID: + pr_info("PID Error\n"); + status = HOST_ESHUTDOWN; + break; + case ERR_TOO_LONG_PACKET: + pr_info("TOO_LONG_PACKET Error\n"); + status = HOST_ESHUTDOWN; + break; + case ERR_DATA_UNDERRUN: + pr_info("UNDERRUN Error\n"); + status = HOST_ESHUTDOWN; + break; + } + + return status; +} + +static struct HOST_EP_PRIV *getIntTransfer(struct list_head *head) +{ + struct list_head *listEntry = NULL; + struct HOST_EP_PRIV *usbHEpPriv = NULL; + struct HOST_EP_PRIV *usbHEpPrivActual = NULL; + + list_for_each(listEntry, head) { + usbHEpPriv = getUsbHEpPrivEntry(listEntry); + if (!usbHEpPrivActual) + usbHEpPrivActual = usbHEpPriv; + + if (usbHEpPriv->frame < usbHEpPrivActual->frame) + usbHEpPrivActual = usbHEpPriv; + } + + return usbHEpPrivActual; +} + +static void givebackRequest(struct HOST_CTRL *priv, struct HOST_REQ *usbReq, uint32_t status) +{ + if (!priv || !usbReq) + return; + + list_del(&usbReq->list); + + if (usbReq->status == EINPROGRESS) + usbReq->status = status; + + if (priv->hostCallbacks.givebackRequest) + priv->hostCallbacks.givebackRequest(priv, usbReq, status); +} + +static void hostEpProgram(struct HOST_CTRL *priv, struct HostEp *hwEp, + struct HOST_REQ *usbReq, uintptr_t dmaBuff, uint32_t length) +{ + struct HOST_EP *usbHEp; + struct HOST_EP_PRIV *usbEpPriv; + uint32_t chMaxLen; + uint8_t regCon = 0; + uint8_t ep0cs; + uint16_t txerrien = 0; + uint16_t rxerrien = 0; + uint32_t result; + uint8_t txsoftimer, rxsoftimer; + u8 retval = 0; + + if (!priv || !hwEp || !usbReq) + return; + + usbHEp = hwEp->scheduledUsbHEp; + usbEpPriv = (struct HOST_EP_PRIV *)usbHEp->hcPriv; + + if (!hwEp->channel) { + if (usbEpPriv->type == USB_ENDPOINT_XFER_ISOC) + hwEp->channel = priv->dmaDrv->dma_channelAlloc(priv->dmaController, + !hwEp->isInEp, hwEp->hwEpNum, 1); + else + hwEp->channel = priv->dmaDrv->dma_channelAlloc(priv->dmaController, + !hwEp->isInEp, hwEp->hwEpNum, 0); + } + + chMaxLen = priv->dmaDrv->dma_getMaxLength(priv->dmaController, hwEp->channel); + + pr_debug("chMaxLen:0x%x buffLength:0x%x\n", chMaxLen, usbReq->buffLength); + if (usbReq->buffLength > chMaxLen) + length = chMaxLen; + + switch (usbEpPriv->type) { + case USB_ENDPOINT_XFER_CONTROL: + regCon = CON_TYPE_CONTROL; + break; + case USB_ENDPOINT_XFER_BULK: + regCon = CON_TYPE_BULK; + break; + case USB_ENDPOINT_XFER_INT: + regCon = CON_TYPE_INT; + break; + case USB_ENDPOINT_XFER_ISOC: + if (usbEpPriv->isocEpConfigured) + goto dma_program; + + usbEpPriv->isocEpConfigured = 1; + regCon = CON_TYPE_ISOC; + switch (usbHEp->desc.wMaxPacketSize >> 11) { + case 0: + regCon |= CON_TYPE_ISOC_1_ISOD; + priv->dmaDrv->dma_setMaxLength(priv->dmaController, + hwEp->channel, usbEpPriv->maxPacketSize); + break; + case 1: + regCon |= CON_TYPE_ISOC_2_ISOD; + priv->dmaDrv->dma_setMaxLength(priv->dmaController, + hwEp->channel, 2 * 1024); + break; + case 2: + priv->dmaDrv->dma_setMaxLength(priv->dmaController, + hwEp->channel, 3 * 1024); + regCon |= CON_TYPE_ISOC_3_ISOD; + break; + } + break; + } + + if (usbEpPriv->type != USB_ENDPOINT_XFER_ISOC) { + if (!hwEp->hwEpNum) { + if (phytium_read8(&priv->regs->ep0cs) & 0x4) { + phytium_write8(&priv->regs->ep0fifoctrl, FIFOCTRL_FIFOAUTO | + 0 | 0x4); + phytium_write8(&priv->regs->ep0fifoctrl, FIFOCTRL_FIFOAUTO | + FIFOCTRL_IO_TX | 0 | 0x4); + } + } + if (waitForBusyBit(priv, hwEp) > 0) { + usbReq->status = HOST_ESHUTDOWN; + givebackRequest(priv, usbReq, HOST_ESHUTDOWN); + pr_info("something error happen\n"); + return; + } + } + + if (!hwEp->isInEp) { + if (hwEp->hwEpNum) { + regCon |= hwEp->hwBuffers - 1; + phytium_write8(&priv->regs->ep[hwEp->hwEpNum - 1].txcon, regCon); + if (usbEpPriv->type != USB_ENDPOINT_XFER_ISOC) { + retval = priv->hostCallbacks.getEpToggle(priv, + usbReq->usbDev, usbEpPriv->epNum, 0); + if (retval) { + phytium_write8(&priv->regs->endprst, hwEp->hwEpNum | + ENDPRST_IO_TX); + + phytium_write8(&priv->regs->endprst, hwEp->hwEpNum | + ENDPRST_TOGSETQ | ENDPRST_IO_TX | ENDPRST_FIFORST); + } else { + phytium_write8(&priv->regs->endprst, hwEp->hwEpNum | + ENDPRST_IO_TX); + + phytium_write8(&priv->regs->endprst, hwEp->hwEpNum | + ENDPRST_TOGRST | ENDPRST_IO_TX | ENDPRST_FIFORST); + } + } + + phytium_write8(&priv->regs->ep[hwEp->hwEpNum - 1].txcon, regCon | CON_VAL); + phytium_write16(&priv->regs->txmaxpack[hwEp->hwEpNum - 1], + usbEpPriv->maxPacketSize); + + phytium_write8(&priv->regs->epExt[hwEp->hwEpNum - 1].txctrl, + usbEpPriv->epNum); + + phytium_write8(&priv->regs->fnaddr, usbEpPriv->faddress); + + // if (usbEpPriv->type == USB_ENDPOINT_XFER_INT) + txsoftimer = phytium_read8(&priv->regs->txsoftimer[hwEp->hwEpNum].ctrl); + txsoftimer = txsoftimer | BIT(1); + phytium_write8(&priv->regs->txsoftimer[hwEp->hwEpNum].ctrl, txsoftimer); + + phytium_write16(&priv->regs->txsoftimer[hwEp->hwEpNum].timer, + usbEpPriv->frame); + + phytium_write8(&priv->regs->txsoftimer[hwEp->hwEpNum].ctrl, 0x83); + } else { + phytium_write8(&priv->regs->fnaddr, usbEpPriv->faddress); + phytium_write8(&priv->regs->ep0maxpack, usbEpPriv->maxPacketSize); + phytium_write8(&priv->regs->ep0ctrl, usbEpPriv->epNum); + + if (priv->ep0State == HOST_EP0_STAGE_SETUP) { + ep0cs = phytium_read8(&priv->regs->ep0cs); + ep0cs |= EP0CS_HCSET; + phytium_write8(&priv->regs->ep0cs, ep0cs); + } + } + + phytium_write16(&priv->regs->txerrirq, 1 << hwEp->hwEpNum); + txerrien = phytium_read16(&priv->regs->txerrien); + txerrien |= 1 << hwEp->hwEpNum; + phytium_write16(&priv->regs->txerrien, txerrien); + } else { + if (hwEp->hwEpNum) { + regCon |= hwEp->hwBuffers - 1; + phytium_write8(&priv->regs->ep[hwEp->hwEpNum - 1].rxcon, regCon); + + if (usbEpPriv->type != USB_ENDPOINT_XFER_ISOC) { + if (priv->hostCallbacks.getEpToggle) { + retval = priv->hostCallbacks.getEpToggle(priv, + usbReq->usbDev, usbEpPriv->epNum, 1); + if (retval) { + phytium_write8(&priv->regs->endprst, hwEp->hwEpNum); + phytium_write8(&priv->regs->endprst, hwEp->hwEpNum | + ENDPRST_TOGSETQ | ENDPRST_FIFORST); + } else { + phytium_write8(&priv->regs->endprst, hwEp->hwEpNum); + phytium_write8(&priv->regs->endprst, hwEp->hwEpNum | + ENDPRST_TOGRST | ENDPRST_FIFORST); + } + } + } + + phytium_write16(&priv->regs->rxmaxpack[hwEp->hwEpNum - 1], + usbEpPriv->maxPacketSize); + + phytium_write8(&priv->regs->epExt[hwEp->hwEpNum - 1].rxctrl, + usbEpPriv->epNum); + + phytium_write8(&priv->regs->fnaddr, usbEpPriv->faddress); + + phytium_write8(&priv->regs->ep[hwEp->hwEpNum - 1].rxcs, 1); + phytium_write8(&priv->regs->ep[hwEp->hwEpNum - 1].rxcs, 1); + phytium_write8(&priv->regs->ep[hwEp->hwEpNum - 1].rxcs, 1); + phytium_write8(&priv->regs->ep[hwEp->hwEpNum - 1].rxcs, 1); + + phytium_write8(&priv->regs->ep[hwEp->hwEpNum - 1].rxcon, regCon | CON_VAL); + rxsoftimer = phytium_read8(&priv->regs->rxsoftimer[hwEp->hwEpNum].ctrl); + rxsoftimer = rxsoftimer | BIT(1); + phytium_write8(&priv->regs->rxsoftimer[hwEp->hwEpNum].ctrl, rxsoftimer); + + phytium_write16(&priv->regs->rxsoftimer[hwEp->hwEpNum].timer, + usbEpPriv->frame); + + phytium_write8(&priv->regs->rxsoftimer[hwEp->hwEpNum].ctrl, 0x83); + } else { + phytium_write8(&priv->regs->fnaddr, usbEpPriv->faddress); + phytium_write8(&priv->regs->ep0maxpack, usbEpPriv->maxPacketSize); + phytium_write8(&priv->regs->ep0ctrl, usbEpPriv->epNum); + + if (priv->ep0State == HOST_EP0_STAGE_IN + || priv->ep0State == HOST_EP0_STAGE_STATUSIN) + phytium_write8(&priv->regs->ep0cs, EP0CS_HCSETTOGGLE); + } + + phytium_write16(&priv->regs->rxerrirq, 1 << hwEp->hwEpNum); + rxerrien = phytium_read16(&priv->regs->rxerrien); + rxerrien |= 1 << hwEp->hwEpNum; + phytium_write16(&priv->regs->rxerrien, rxerrien); + } +dma_program: + result = priv->dmaDrv->dma_channelProgram(priv->dmaController, hwEp->channel, + usbEpPriv->maxPacketSize, dmaBuff, length, + (void *)usbReq->isoFramesDesc, usbReq->isoFramesNumber); + if (result) { + if (!hwEp->isInEp) { + txerrien &= ~(1 << hwEp->hwEpNum); + if (hwEp->hwEpNum) + phytium_write8(&priv->regs->ep[hwEp->hwEpNum - 1].txcon, 0x08); + phytium_write16(&priv->regs->txerrien, txerrien); + } else { + rxerrien &= ~(1 << hwEp->hwEpNum); + if (hwEp->hwEpNum) + phytium_write8(&priv->regs->ep[hwEp->hwEpNum - 1].rxcon, 0x08); + phytium_write16(&priv->regs->rxerrien, rxerrien); + } + + priv->dmaDrv->dma_channelRelease(priv->dmaController, hwEp->channel); + hwEp->channel = NULL; + } +} + +static void hostStartReq(struct HOST_CTRL *priv, struct HOST_REQ *req) +{ + uintptr_t dmaBuff; + uint32_t length; + struct HOST_EP *hostEp; + struct HOST_EP_PRIV *hostEpPriv; + struct HOST_EP_PRIV *hostNewEpPriv; + struct HOST_REQ *usbReq = NULL; + struct HostEp *hwEp = NULL; + int num; + + if (!priv || !req) + return; + + hostEp = req->hcPriv; + if (hostEp) { + hostEpPriv = (struct HOST_EP_PRIV *)hostEp->hcPriv; + if (hostEpPriv) { + hostEpPriv->genericHwEp->state = HOST_EP_BUSY; + switch (hostEpPriv->type) { + case USB_ENDPOINT_XFER_CONTROL: + usbReq = getNextReq(hostEp); + + priv->in[HOST_GENERIC_EP_CONTROLL].scheduledUsbHEp = hostEp; + priv->ep0State = HOST_EP0_STAGE_SETUP; + hostEpPriv->currentHwEp = hostEpPriv->genericHwEp; + hostEpPriv->genericHwEp->scheduledUsbHEp = hostEp; + dmaBuff = usbReq->setupDma; + length = 8; + pr_debug("packet info: %02x %02x %04x %04x %04x\n", + usbReq->setup->bRequestType, + usbReq->setup->bRequest, + usbReq->setup->wValue, + usbReq->setup->wIndex, + usbReq->setup->wLength); + hostEpProgram(priv, hostEpPriv->genericHwEp, + usbReq, dmaBuff, length); + break; + case USB_ENDPOINT_XFER_BULK: + hwEp = hostEpPriv->genericHwEp; + usbReq = getNextReq(hostEp); + hostEpPriv->currentHwEp = hwEp; + hwEp->scheduledUsbHEp = hostEp; + dmaBuff = usbReq->buffDma + usbReq->actualLength; + length = usbReq->buffLength - usbReq->actualLength; + hostEpProgram(priv, hwEp, usbReq, dmaBuff, length); + break; + case USB_ENDPOINT_XFER_INT: + if (hostEpPriv->genericHwEp->scheduledUsbHEp) + return; + + num = req->epNum - 1; + if (hostEpPriv->isIn) + hostNewEpPriv = getIntTransfer(&priv->intInHEpQueue[num]); + else + hostNewEpPriv = getIntTransfer(&priv->intOutHEpQueue[num]); + + hostNewEpPriv->currentHwEp = hostEpPriv->genericHwEp; + hostEpPriv->genericHwEp->scheduledUsbHEp = hostNewEpPriv->usbHEp; + usbReq = getNextReq(hostEp); + dmaBuff = usbReq->buffDma + usbReq->actualLength; + length = usbReq->buffLength - usbReq->actualLength; + hostEpProgram(priv, hostEpPriv->genericHwEp, + usbReq, dmaBuff, length); + break; + case USB_ENDPOINT_XFER_ISOC: + hostEpPriv->currentHwEp = hostEpPriv->genericHwEp; + hostEpPriv->genericHwEp->scheduledUsbHEp = hostEp; + dmaBuff = req->buffDma + req->actualLength; + length = req->buffLength - req->actualLength; + hostEpProgram(priv, hostEpPriv->genericHwEp, req, dmaBuff, length); + break; + } + } + } +} + + +static void scheduleNextTransfer(struct HOST_CTRL *priv, + struct HOST_REQ *usbReq, struct HostEp *hwEp) +{ + struct HOST_EP *usbEp; + struct HOST_EP_PRIV *usbHEpPriv; + uint8_t endprst; + uint32_t status; + struct HOST_REQ *usbNextReq = NULL; + + if (!priv || !usbReq || !hwEp) + return; + + usbEp = hwEp->scheduledUsbHEp; + usbHEpPriv = (struct HOST_EP_PRIV *)usbEp->hcPriv; + if (!usbHEpPriv) + return; + + status = (usbReq->status == EINPROGRESS) ? 0 : usbReq->status; + switch (usbHEpPriv->type) { + case USB_ENDPOINT_XFER_BULK: + case USB_ENDPOINT_XFER_INT: + if (hwEp->isInEp) { + phytium_write8(&priv->regs->endprst, hwEp->hwEpNum); + endprst = (phytium_read8(&priv->regs->endprst) & ENDPRST_TOGSETQ) ? 1 : 0; + if (priv->hostCallbacks.setEpToggle) + priv->hostCallbacks.setEpToggle(priv, usbReq->usbDev, + usbHEpPriv->epNum, usbHEpPriv->isIn, endprst); + } else { + if (waitForBusyBit(priv, hwEp) > 0) { + usbReq->status = HOST_ESHUTDOWN; + givebackRequest(priv, usbReq, HOST_ESHUTDOWN); + return; + } + + phytium_write8(&priv->regs->endprst, hwEp->hwEpNum | ENDPRST_IO_TX); + endprst = (phytium_read8(&priv->regs->endprst) & ENDPRST_TOGSETQ) ? 1 : 0; + if (priv->hostCallbacks.setEpToggle) + priv->hostCallbacks.setEpToggle(priv, usbReq->usbDev, + usbHEpPriv->epNum, usbHEpPriv->isIn, endprst); + } + break; + } + + if (usbHEpPriv->transferFinished) + givebackRequest(priv, usbReq, status); + + if (list_empty(&usbEp->reqList)) { + if (usbHEpPriv->type == USB_ENDPOINT_XFER_CONTROL) + hwEp->state = HOST_EP_ALLOCATED; + else { + if (usbHEpPriv->genericHwEp == hwEp) + hwEp->state = HOST_EP_ALLOCATED; + else + hwEp->state = HOST_EP_FREE; + } + + usbHEpPriv->epIsReady = 0; + usbHEpPriv->currentHwEp = NULL; + hwEp->scheduledUsbHEp = NULL; + + if (hwEp->channel) { + priv->dmaDrv->dma_channelRelease(priv->dmaController, hwEp->channel); + hwEp->channel = NULL; + } + + if (usb_endpoint_xfer_int(&usbEp->desc)) + list_del(&usbHEpPriv->node); + usbHEpPriv = NULL; + } else { + if (usbHEpPriv->type == USB_ENDPOINT_XFER_INT) { + usbHEpPriv->currentHwEp = NULL; + hwEp->scheduledUsbHEp = NULL; + } + + if (usbHEpPriv->type == USB_ENDPOINT_XFER_ISOC) + return; + } + + if (usbHEpPriv) { + usbNextReq = getNextReq(usbHEpPriv->usbHEp); + hostStartReq(priv, usbNextReq); + } +} + +static int32_t hostEp0Irq(struct HOST_CTRL *priv, uint8_t isIn) +{ + struct HostEp *hwEp; + struct HOST_EP *hostEp; + struct HOST_REQ *usbReq = NULL; + struct HOST_EP_PRIV *usbHEpPriv; + uint32_t status, length; + uint8_t usbError; + uint8_t nextStage = 0; + int ret = 0; + + if (!priv) + return ret; + + hwEp = isIn ? &priv->in[HOST_GENERIC_EP_CONTROLL] : &priv->out[HOST_GENERIC_EP_CONTROLL]; + hostEp = hwEp->scheduledUsbHEp; + usbHEpPriv = (struct HOST_EP_PRIV *)hostEp->hcPriv; + + usbReq = getNextReq(hostEp); + if (!usbReq) + return 0; + + usbError = isIn ? phytium_read8(&priv->regs->rx0err) : phytium_read8(&priv->regs->tx0err); + usbError = (usbError & ERR_TYPE) >> 2; + status = decodeErrorCode(usbError); + if (status) { + usbReq->status = status; + + if (status == HOST_ESTALL) { + ret = 1; + priv->dmaDrv->dma_controllerReset(priv->dmaController); + } + + phytium_write16(&priv->regs->rxerrirq, 1 << hwEp->hwEpNum); + phytium_write16(&priv->regs->txerrirq, 1 << hwEp->hwEpNum); + + phytium_write8(&priv->regs->ep0fifoctrl, FIFOCTRL_FIFOAUTO | 0 | 0x4); + phytium_write8(&priv->regs->ep0fifoctrl, FIFOCTRL_FIFOAUTO | + FIFOCTRL_IO_TX | 0 | 0x4); + } + + length = priv->dmaDrv->dma_getActualLength(priv->dmaController, hwEp->channel); + priv->dmaDrv->dma_channelRelease(priv->dmaController, hwEp->channel); + hwEp->channel = NULL; + + if (usbReq->status == EINPROGRESS && priv->ep0State < HOST_EP0_STAGE_STATUSIN) { + nextStage = 0; + switch (priv->ep0State) { + case HOST_EP0_STAGE_IN: + pr_debug("Ep0 Data IN\n"); + usbHEpPriv->currentHwEp = &priv->out[HOST_GENERIC_EP_CONTROLL]; + usbReq->actualLength = length; + priv->ep0State = HOST_EP0_STAGE_STATUSOUT; + break; + case HOST_EP0_STAGE_OUT: + pr_debug("Ep0 Data OUT\n"); + usbHEpPriv->currentHwEp = &priv->in[HOST_GENERIC_EP_CONTROLL]; + usbReq->actualLength = length; + priv->ep0State = HOST_EP0_STAGE_STATUSIN; + break; + case HOST_EP0_STAGE_SETUP: + pr_debug("Ep0 Stage Setup\n"); + if (!usbReq->setup->wLength) { + pr_debug("EP0_STAGE_STATUSIN\n"); + priv->ep0State = HOST_EP0_STAGE_STATUSIN; + usbHEpPriv->currentHwEp = &priv->in[HOST_GENERIC_EP_CONTROLL]; + break; + } else if (usbReq->setup->bRequestType & USB_DIR_IN) { + pr_debug("EP0_STAGE_STAGE_IN\n"); + priv->ep0State = HOST_EP0_STAGE_IN; + usbHEpPriv->currentHwEp = &priv->in[HOST_GENERIC_EP_CONTROLL]; + nextStage = 1; + break; + } + priv->ep0State = HOST_EP0_STAGE_OUT; + nextStage = 1; + break; + case HOST_EP0_STAGE_STATUSIN: + case HOST_EP0_STAGE_STATUSOUT: + case HOST_EP0_STAGE_ACK: + case HOST_EP0_STAGE_IDLE: + default: + pr_debug("EP0 STAGE is %d\n", priv->ep0State); + break; + } + + if (nextStage) { + length = usbReq->buffLength; + hostEpProgram(priv, usbHEpPriv->currentHwEp, usbReq, + usbReq->buffDma, length); + } else + hostEpProgram(priv, usbHEpPriv->currentHwEp, usbReq, usbReq->setupDma, 0); + } else + priv->ep0State = HOST_EP0_STAGE_IDLE; + + if (priv->ep0State == HOST_EP0_STAGE_IDLE || usbReq->status != EINPROGRESS) { + usbHEpPriv->transferFinished = 1; + scheduleNextTransfer(priv, usbReq, hwEp); + } + + return 0; +} + +static void updateTimeIntTransfer(struct list_head *head, struct HOST_EP_PRIV *lastFinished) +{ + struct list_head *listEntry = NULL; + struct HOST_EP_PRIV *usbHEpPriv = NULL; + uint16_t time = lastFinished->frame; + + list_for_each(listEntry, head) { + usbHEpPriv = getUsbHEpPrivEntry(listEntry); + if (usbHEpPriv == lastFinished) { + lastFinished->frame = lastFinished->interval; + continue; + } + + if (usbHEpPriv->frame < time) + usbHEpPriv->frame = 0; + else + lastFinished->interval = usbHEpPriv->frame; + } +} + +static int32_t hostEpXIrq(struct HOST_CTRL *priv, uint8_t hwEpNum, uint8_t isIn, bool resubmit) +{ + struct HostEp *hwEp; + struct HOST_EP *hostEp; + struct HOST_EP_PRIV *usbHEpPriv; + struct HOST_REQ *usbReq; + + uint8_t usbError, rxcon, txcon; + uint32_t status, length, chMaxLen; + + if (!priv) + return -EINVAL; + + hwEp = isIn ? &priv->in[hwEpNum] : &priv->out[hwEpNum]; + hostEp = hwEp->scheduledUsbHEp; + if (!hostEp) + return -EINVAL; + + usbHEpPriv = (struct HOST_EP_PRIV *)hostEp->hcPriv; + + usbReq = getNextReq(hostEp); + if (!usbReq) + return 0; + + if (isIn) + usbError = phytium_read8(&priv->regs->epExt[hwEpNum - 1].rxerr); + else + usbError = phytium_read8(&priv->regs->epExt[hwEpNum - 1].txerr); + + usbError = (usbError & ERR_TYPE) >> 2; + status = decodeErrorCode(usbError); + if (status) { + pr_debug("%s %d Aborting\n", __func__, __LINE__); + if (isIn) + phytium_write16(&priv->regs->rxerrirq, 1 << hwEpNum); + else + phytium_write16(&priv->regs->txerrirq, 1 << hwEpNum); + priv->dmaDrv->dma_channelAbort(priv->dmaController, hwEp->channel); + } + + length = priv->dmaDrv->dma_getActualLength(priv->dmaController, hwEp->channel); + chMaxLen = priv->dmaDrv->dma_getMaxLength(priv->dmaController, hwEp->channel); + + if (status != 0) + usbReq->status = status; + + usbReq->actualLength += length; + + if (!resubmit) + usbHEpPriv->transferFinished = 1; + + if (length == chMaxLen) { + if ((usbReq->buffLength - usbReq->actualLength) == 0) + usbHEpPriv->transferFinished = 1; + else + usbHEpPriv->transferFinished = 0; + } + + if (usbHEpPriv->type != USB_ENDPOINT_XFER_ISOC) { + if (!hwEp->isInEp) { + txcon = phytium_read8(&priv->regs->ep[hwEp->hwEpNum - 1].txcon); + txcon &= ~CON_VAL; + phytium_write8(&priv->regs->ep[hwEp->hwEpNum - 1].txcon, txcon); + } else { + rxcon = phytium_read8(&priv->regs->ep[hwEp->hwEpNum - 1].rxcon); + rxcon &= ~CON_VAL; + phytium_write8(&priv->regs->ep[hwEp->hwEpNum - 1].rxcon, rxcon); + } + priv->dmaDrv->dma_channelRelease(priv->dmaController, hwEp->channel); + hwEp->channel = NULL; + } + + if (usbHEpPriv->type == USB_ENDPOINT_XFER_INT) { + if (usbHEpPriv->isIn) + updateTimeIntTransfer(&priv->intInHEpQueue[hwEp->hwEpNum - 1], usbHEpPriv); + else + updateTimeIntTransfer(&priv->intOutHEpQueue[hwEp->hwEpNum - 1], usbHEpPriv); + } + + scheduleNextTransfer(priv, usbReq, hwEp); + + return 0; +} + + +static void host_CallbackTransfer(void *priv, uint8_t epNum, uint8_t isDirTx, bool resubmit) +{ + struct HOST_CTRL *host_priv = (struct HOST_CTRL *)priv; + int i; + int ret = 0; + + if (!epNum) { + ret = hostEp0Irq(host_priv, !isDirTx); + if (resubmit | ret) { + for (i = 1; i < HOST_EP_NUM; i++) { + hostEpXIrq(host_priv, i, !isDirTx, true); + hostEpXIrq(host_priv, i, isDirTx, true); + } + } + } else + hostEpXIrq(host_priv, epNum, !isDirTx, false); +} + +static int hc_reset(struct usb_hcd *hcd) +{ + hcd->speed = HCD_USB2; + hcd->self.root_hub->speed = USB_SPEED_HIGH; + + return 0; +} + +static int hc_start(struct usb_hcd *hcd) +{ + hcd->state = HC_STATE_RUNNING; + return 0; +} + +static void hc_stop(struct usb_hcd *hcd) +{ + struct phytium_cusb *config = *(struct phytium_cusb **)hcd->hcd_priv; + + if (config) + config->host_obj->host_stop(config->host_priv); + + if (config->host_cfg.trbAddr) { + dma_free_coherent(config->dev, config->host_sysreq.trbMemSize, + config->host_cfg.trbAddr, config->host_cfg.trbDmaAddr); + config->host_cfg.trbAddr = NULL; + } + + if (config->host_priv) { + devm_kfree(config->dev, config->host_priv); + config->host_priv = NULL; + } + + hcd->state = HC_STATE_HALT; +} + +static void hc_shutdown(struct usb_hcd *hcd) +{ +} + +static void host_endpoint_update(struct phytium_cusb *config, + struct HOST_USB_DEVICE *udev, struct usb_host_endpoint *ep) +{ + int epnum; + struct HOST_EP *hostEp; + + if (!config || !udev || !ep) + return; + + epnum = usb_endpoint_num(&ep->desc); + if (epnum > MAX_INSTANCE_EP_NUM) + epnum = MAX_INSTANCE_EP_NUM; + + if (usb_endpoint_dir_out(&ep->desc)) { + if (udev->out_ep[epnum] == NULL) { + hostEp = kzalloc(sizeof(struct HOST_EP) + + config->host_obj->host_epGetPrivateDataSize(config->host_priv), + GFP_ATOMIC); + udev->out_ep[epnum] = hostEp; + } else + hostEp = udev->out_ep[epnum]; + } else { + if (udev->in_ep[epnum] == NULL) { + hostEp = kzalloc(sizeof(struct HOST_EP) + + config->host_obj->host_epGetPrivateDataSize(config->host_priv), + GFP_ATOMIC); + udev->in_ep[epnum] = hostEp; + } else + hostEp = udev->in_ep[epnum]; + } + + hostEp->desc = *(struct usb_endpoint_descriptor *)(&ep->desc); + hostEp->userExt = (void *)ep; + INIT_LIST_HEAD(&hostEp->reqList); + hostEp->hcPriv = &((uint8_t *)hostEp)[sizeof(struct HOST_EP)]; +} + +static int hc_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) +{ + unsigned long flags; + u32 isoFrameSize; + int retval; + int index; + struct HOST_REQ *req; + struct HOST_CTRL *priv; + struct HOST_USB_DEVICE *usbDev; + struct usb_host_endpoint *host_ep; + struct usb_endpoint_descriptor *host_ep_desc; + struct phytium_cusb *config = *(struct phytium_cusb **)hcd->hcd_priv; + + if (!config) + return -ENODEV; + + priv = config->host_priv; + if (!priv) + return -ENODEV; + + usbDev = priv->host_devices_table[urb->dev->slot_id]; + if (!usbDev) + return -ENODEV; + + host_ep = urb->ep; + host_ep_desc = &host_ep->desc; + + spin_lock_irqsave(&config->lock, flags); + retval = usb_hcd_link_urb_to_ep(hcd, urb); + if (retval < 0) { + spin_unlock_irqrestore(&config->lock, flags); + return retval; + } + spin_unlock_irqrestore(&config->lock, flags); + isoFrameSize = urb->number_of_packets * sizeof(struct HOST_ISOFRAMESDESC); + req = kzalloc((sizeof(struct HOST_REQ) + + isoFrameSize), mem_flags); + if (!req) { + spin_lock_irqsave(&config->lock, flags); + usb_hcd_unlink_urb_from_ep(hcd, urb); + spin_unlock_irqrestore(&config->lock, flags); + return -ENOMEM; + } + + req->isoFramesDesc = NULL; + req->isoFramesNumber = urb->number_of_packets; + req->epNum = usb_endpoint_num(host_ep_desc); + if (req->epNum > MAX_INSTANCE_EP_NUM) + req->epNum = MAX_INSTANCE_EP_NUM; + + if (usb_endpoint_dir_in(host_ep_desc)) { + if (!usbDev->in_ep[req->epNum]) + host_endpoint_update(config, usbDev, host_ep); + } else { + if (!usbDev->out_ep[req->epNum]) + host_endpoint_update(config, usbDev, host_ep); + } + + if (usb_endpoint_xfer_isoc(host_ep_desc)) { + req->isoFramesDesc = (struct HOST_ISOFRAMESDESC *)(&req[1]); + for (index = 0; index < urb->number_of_packets; index++) { + req->isoFramesDesc[index].length = urb->iso_frame_desc[index].length; + req->isoFramesDesc[index].offset = urb->iso_frame_desc[index].offset; + } + } + + spin_lock_irqsave(&config->lock, flags); + INIT_LIST_HEAD(&req->list); + req->userExt = (void *)urb; + req->actualLength = urb->actual_length; + req->bufAddress = urb->transfer_buffer; + req->buffDma = urb->transfer_dma; + req->buffLength = urb->transfer_buffer_length; + req->epIsIn = usb_endpoint_dir_in(host_ep_desc); + req->eptype = usb_endpoint_type(host_ep_desc); + req->faddress = usb_pipedevice(urb->pipe); + req->interval = urb->interval; + req->reqUnlinked = 0; + req->setup = (struct usb_ctrlrequest *)urb->setup_packet; + req->setupDma = urb->setup_dma; + req->status = EINPROGRESS; + req->usbDev = &usbDev->udev; + req->usbEp = req->epIsIn ? usbDev->in_ep[req->epNum] : usbDev->out_ep[req->epNum]; + if (!req->epNum) + usbDev->ep0_hep.desc.wMaxPacketSize = urb->dev->ep0.desc.wMaxPacketSize; + + req->hcPriv = (void *)req->usbEp; + urb->hcpriv = req; + host_ep->hcpriv = (void *)usbDev; + retval = config->host_obj->host_reqQueue(config->host_priv, req); + if (retval) { + usb_hcd_unlink_urb_from_ep(hcd, urb); + urb->hcpriv = NULL; + spin_unlock_irqrestore(&config->lock, flags); + kfree(req); + return -retval; + } + spin_unlock_irqrestore(&config->lock, flags); + + return 0; +} + +static int hc_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) +{ + unsigned long flags; + struct HOST_CTRL *priv; + int ret = 0; + struct phytium_cusb *config = *(struct phytium_cusb **)hcd->hcd_priv; + + if (!config) + return -ENODEV; + + priv = config->host_priv; + if (!priv->host_devices_table[urb->dev->slot_id]) + return -ENODEV; + + spin_lock_irqsave(&config->lock, flags); + if (usb_hcd_check_unlink_urb(hcd, urb, status)) + goto done; + + if (!urb->hcpriv) + goto err_giveback; + + ret = config->host_obj->host_reqDequeue(priv, urb->hcpriv, status); + kfree(urb->hcpriv); + urb->hcpriv = NULL; +done: + spin_unlock_irqrestore(&config->lock, flags); + return ret; + +err_giveback: + kfree(urb->hcpriv); + usb_hcd_unlink_urb_from_ep(hcd, urb); + spin_unlock_irqrestore(&config->lock, flags); + usb_hcd_giveback_urb(hcd, urb, -ESHUTDOWN); + return ret; +} + +static void hc_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *ld_ep) +{ + struct HOST_USB_DEVICE *usbDev; + int ep_num = usb_endpoint_num(&ld_ep->desc); + + if (ep_num > MAX_INSTANCE_EP_NUM) + ep_num = MAX_INSTANCE_EP_NUM; + + usbDev = (struct HOST_USB_DEVICE *)ld_ep->hcpriv; + if (!usbDev) + return; + + if (ld_ep->desc.bEndpointAddress) { + if (usb_endpoint_dir_in(&ld_ep->desc)) { + if (!usbDev->in_ep[ep_num]) { + usbDev->in_ep[ep_num]->userExt = NULL; + INIT_LIST_HEAD(&usbDev->in_ep[ep_num]->reqList); + kfree(usbDev->in_ep[ep_num]); + usbDev->in_ep[ep_num] = NULL; + } + } else { + if (!usbDev->out_ep[ep_num]) { + usbDev->out_ep[ep_num]->userExt = NULL; + INIT_LIST_HEAD(&usbDev->out_ep[ep_num]->reqList); + kfree(usbDev->out_ep[ep_num]); + usbDev->out_ep[ep_num] = NULL; + } + } + } +} + +static int hc_get_frame(struct usb_hcd *hcd) +{ + return 0; +} + +static int hc_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev) +{ + struct HOST_USB_DEVICE *usbDev; + struct phytium_cusb *config; + struct HOST_CTRL *priv; + unsigned long flags = 0; + int index; + int slot = -EINVAL; + + if (!hcd || !udev) + return -EINVAL; + + config = *(struct phytium_cusb **)hcd->hcd_priv; + if (!config) + return 0; + + spin_lock_irqsave(&config->lock, flags); + priv = config->host_priv; + for (index = 0; index < MAX_SUPPORTED_DEVICES; index++) { + if (priv->host_devices_table[index] == NULL) { + slot = index; + break; + } + } + spin_unlock_irqrestore(&config->lock, flags); + + if (slot < 0) + return -ENOMEM; + + usbDev = kzalloc((sizeof(struct HOST_USB_DEVICE) + + config->host_obj->host_epGetPrivateDataSize(priv)), GFP_KERNEL); + if (!usbDev) + return -ENOMEM; + + usbDev->ep0_hep.hcPriv = &((uint8_t *)usbDev)[sizeof(struct HOST_USB_DEVICE)]; + usbDev->udev.userExt = (void *)usbDev; + usbDev->ld_udev = udev; + usbDev->ld_udev->slot_id = slot; + usbDev->ep0_hep.desc.bLength = 7; + usbDev->ep0_hep.desc.bDescriptorType = USB_DT_ENDPOINT; + usbDev->in_ep[0] = &usbDev->ep0_hep; + usbDev->out_ep[0] = &usbDev->ep0_hep; + INIT_LIST_HEAD(&usbDev->ep0_hep.reqList); + + priv->host_devices_table[slot] = usbDev; + + return 1; +} + +static void hc_free_dev(struct usb_hcd *hcd, struct usb_device *udev) +{ + struct HOST_CTRL *priv; + struct HOST_USB_DEVICE *usbDev; + struct phytium_cusb *config; + int i; + + if (!hcd || !udev) + return; + + config = *(struct phytium_cusb **)(hcd->hcd_priv); + if (!config) + return; + + priv = (struct HOST_CTRL *)config->host_priv; + usbDev = priv->host_devices_table[udev->slot_id]; + if (!usbDev) + return; + + usbDev->in_ep[0] = NULL; + usbDev->out_ep[0] = NULL; + for (i = 1; i < HOST_EP_NUM; i++) { + if (usbDev->in_ep[i]) + hc_endpoint_disable(hcd, + (struct usb_host_endpoint *)usbDev->in_ep[i]->userExt); + if (usbDev->out_ep[i]) + hc_endpoint_disable(hcd, + (struct usb_host_endpoint *)usbDev->out_ep[i]->userExt); + } + + priv->host_devices_table[udev->slot_id] = NULL; + usbDev->ld_udev->slot_id = 0; + usbDev->udev.userExt = (void *)NULL; + kfree(usbDev); +} + +static int hc_reset_device(struct usb_hcd *hcd, struct usb_device *udev) +{ + struct HOST_CTRL *priv; + struct HOST_USB_DEVICE *usb_device; + struct phytium_cusb *config; + + if (!hcd || !udev) + return -EINVAL; + + config = *(struct phytium_cusb **)hcd->hcd_priv; + priv = (struct HOST_CTRL *)config->host_priv; + usb_device = priv->host_devices_table[udev->slot_id]; + if (!usb_device) + return -ENODEV; + + usb_device->udev.speed = usb_device->ld_udev->speed; + usb_device->udev.devnum = usb_device->ld_udev->devnum; + + if (USB_SPEED_HIGH == usb_device->udev.speed || USB_SPEED_FULL == usb_device->udev.speed) + usb_device->ep0_hep.desc.wMaxPacketSize = 64; + else + usb_device->ep0_hep.desc.wMaxPacketSize = 8; + + pr_debug("speed:%d ep0 wMaxPacketSize:%d\n", usb_device->udev.speed, + usb_device->ep0_hep.desc.wMaxPacketSize); + + return 0; +} + +static int hc_update_device(struct usb_hcd *hcd, struct usb_device *udev) +{ + struct HOST_CTRL *priv; + struct HOST_USB_DEVICE *usb_device; + struct phytium_cusb *config; + + if (!hcd || !udev) + return -EINVAL; + + config = *(struct phytium_cusb **)(hcd->hcd_priv); + priv = (struct HOST_CTRL *)config->host_priv; + + usb_device = priv->host_devices_table[udev->slot_id]; + if (!usb_device) + return -ENODEV; + + usb_device->udev.devnum = udev->devnum; + + return 0; +} + +static int hc_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev, + struct usb_host_endpoint *ep) +{ + return 0; +} + +static int hc_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev, + struct usb_host_endpoint *ep) +{ + return 0; +} + +static int hc_hub_status_data(struct usb_hcd *hcd, char *buf) +{ + struct HOST_CTRL *priv; + struct phytium_cusb *config = *(struct phytium_cusb **)hcd->hcd_priv; + + if (!config) + return 0; + + priv = (struct HOST_CTRL *)config->host_priv; + if (!priv) + return 0; + + if (priv->portStatus & 0xffff0000) { + *buf = 0x02; + return 1; + } + + return 0; +} + +#ifdef CONFIG_PM +static int hc_bus_suspend(struct usb_hcd *hcd) +{ + unsigned long flags; + struct phytium_cusb *config = *(struct phytium_cusb **)(hcd->hcd_priv); + + if (!config) + return 0; + + spin_lock_irqsave(&config->lock, flags); + hcd->state = HC_STATE_SUSPENDED; + spin_unlock_irqrestore(&config->lock, flags); + + return 0; +} + +static int hc_bus_resume(struct usb_hcd *hcd) +{ + unsigned long flags; + struct phytium_cusb *config = *(struct phytium_cusb **)(hcd->hcd_priv); + + if (!config) + return 0; + + spin_lock_irqsave(&config->lock, flags); + hcd->state = HC_STATE_RESUMING; + spin_unlock_irqrestore(&config->lock, flags); + return 0; +} +#endif + +static void host_giveback_request(struct HOST_CTRL *priv, + struct HOST_REQ *req, uint32_t status) +{ + struct urb *urb_req; + int urb_status = 0; + int i = 0; + struct phytium_cusb *config; + + if (!priv || !req) + return; + + urb_req = req->userExt; + urb_req->actual_length = req->actualLength; + + switch (status) { + case HOST_ESTALL: + urb_status = -EPIPE; + break; + case HOST_EUNHANDLED: + urb_status = -EPROTO; + break; + case HOST_ESHUTDOWN: + urb_status = -ESHUTDOWN; + break; + default: + urb_status = status; + } + pr_debug("complete %p %pS (%d) dev%d ep%d%s %d/%d\n", + urb_req, urb_req->complete, urb_status, + usb_pipedevice(urb_req->pipe), + usb_pipeendpoint(urb_req->pipe), + usb_pipein(urb_req->pipe) ? "in" : "out", + urb_req->actual_length, + urb_req->transfer_buffer_length); + + if (usb_endpoint_xfer_isoc(&urb_req->ep->desc)) { + for (i = 0; i < urb_req->number_of_packets; i++) { + urb_req->iso_frame_desc[i].status = 0; + urb_req->iso_frame_desc[i].actual_length = req->isoFramesDesc[i].length; + } + } + + config = dev_get_drvdata(priv->dev); + usb_hcd_unlink_urb_from_ep(config->hcd, urb_req); + spin_unlock(&config->lock); + usb_hcd_giveback_urb(config->hcd, urb_req, urb_status); + kfree(req); + spin_lock(&config->lock); +} + +static void host_rh_port_status_change(struct HOST_CTRL *priv) +{ + uint32_t statusHub; + char *status; + uint32_t retval; + struct usb_ctrlrequest setup; + struct phytium_cusb *config; + + if (!priv) + return; + + config = dev_get_drvdata(priv->dev); + if (!config) + return; + + status = (char *)&statusHub; + hc_hub_status_data(config->hcd, status); + + if (status) { + setup.bRequestType = USB_TYPE_CLASS | USB_RECIP_OTHER | USB_DIR_IN;//to host + setup.bRequest = USB_REQ_GET_STATUS; + setup.wValue = 0; + setup.wIndex = 1; + setup.wLength = 4; + retval = config->host_obj->host_vhubControl(priv, &setup, (uint8_t *)&statusHub); + if (retval) + return; + + if (status[1] & USB_PORT_STAT_C_CONNECTION) { + if (status[0] & USB_PORT_STAT_CONNECTION) { + if (config->hcd->status_urb) + usb_hcd_poll_rh_status(config->hcd); + else + usb_hcd_resume_root_hub(config->hcd); + } else { + usb_hcd_resume_root_hub(config->hcd); + usb_hcd_poll_rh_status(config->hcd); + } + } + } else + usb_hcd_resume_root_hub(config->hcd); +} + +static void host_set_ep_toggle(struct HOST_CTRL *priv, + struct HOST_DEVICE *udev, u8 ep_num, u8 is_in, u8 toggle) +{ + struct HOST_USB_DEVICE *device; + + if (!priv || !udev) + return; + + device = (struct HOST_USB_DEVICE *)udev->userExt; + + usb_settoggle(device->ld_udev, ep_num, !is_in, toggle); +} + +static u8 host_get_ep_toggle(struct HOST_CTRL *priv, + struct HOST_DEVICE *udev, u8 ep_num, u8 is_in) +{ + struct HOST_USB_DEVICE *device; + + if (!priv || !udev) + return 0; + + device = (struct HOST_USB_DEVICE *)udev->userExt; + + return usb_gettoggle(device->ld_udev, ep_num, !is_in); +} + +static uint32_t initEndpoints(struct HOST_CTRL *priv) +{ + int epNum; + + if (!priv) + return 0; + + priv->hwEpInCount = 0; + priv->hwEpOutCount = 0; + phytium_write8(&priv->regs->ep0fifoctrl, FIFOCTRL_FIFOAUTO | 0); + phytium_write8(&priv->regs->ep0fifoctrl, FIFOCTRL_FIFOAUTO | FIFOCTRL_IO_TX | 0); + + for (epNum = 0; epNum < HOST_EP_NUM; epNum++) { + priv->in[epNum].isInEp = 1; + priv->in[epNum].hwEpNum = epNum; + if (priv->hostCfg.epIN[epNum].bufferingValue) { + priv->in[epNum].hwMaxPacketSize = priv->hostCfg.epIN[epNum].maxPacketSize; + priv->in[epNum].hwBuffers = priv->hostCfg.epIN[epNum].bufferingValue; + priv->in[epNum].state = HOST_EP_FREE; + priv->in[epNum].channel = NULL; + priv->hwEpInCount++; + + if (epNum) { + phytium_write16(&priv->regs->rxstaddr[epNum - 1].addr, + priv->hostCfg.epIN[epNum].startBuf); + phytium_write8(&priv->regs->ep[epNum - 1].rxcon, 0x08); + phytium_write8(&priv->regs->fifoctrl, FIFOCTRL_FIFOAUTO | epNum); + phytium_write8(&priv->regs->irqmode[epNum - 1].inirqmode, 0x80); + } + } else + priv->in[epNum].state = HOST_EP_NOT_IMPLEMENTED; + + priv->out[epNum].isInEp = 0; + priv->out[epNum].hwEpNum = epNum; + if (priv->hostCfg.epOUT[epNum].bufferingValue) { + priv->out[epNum].hwMaxPacketSize = priv->hostCfg.epOUT[epNum].maxPacketSize; + priv->out[epNum].hwBuffers = priv->hostCfg.epOUT[epNum].bufferingValue; + priv->out[epNum].state = HOST_EP_FREE; + priv->out[epNum].channel = NULL; + priv->hwEpInCount++; + + if (epNum) { + phytium_write16(&priv->regs->txstaddr[epNum - 1].addr, + priv->hostCfg.epOUT[epNum].startBuf); + phytium_write8(&priv->regs->ep[epNum - 1].txcon, 0x08); + phytium_write8(&priv->regs->fifoctrl, + FIFOCTRL_FIFOAUTO | FIFOCTRL_IO_TX | epNum); + phytium_write8(&priv->regs->irqmode[epNum - 1].outirqmode, 0x80); + } + } else + priv->out[epNum].state = HOST_EP_NOT_IMPLEMENTED; + } + + return 0; +} + +int32_t hostInit(struct HOST_CTRL *priv, struct HOST_CFG *config, + struct HOST_CALLBACKS *callbacks, struct device *pdev, bool isVhubHost) +{ + int index; + + if (!config || !priv || !callbacks || !pdev) + return -EINVAL; + + priv->dev = pdev; + priv->hostCallbacks = *callbacks; + priv->hostCfg = *config; + priv->regs = (struct HW_REGS *)config->regBase; + priv->hostDrv = HOST_GetInstance(); + + priv->dmaDrv = DMA_GetInstance(); + priv->dmaCfg.dmaModeRx = 0xFFFF; + priv->dmaCfg.dmaModeTx = 0xFFFF; + priv->dmaCfg.regBase = config->regBase + 0x400; + priv->dmaCfg.trbAddr = config->trbAddr; + priv->dmaCfg.trbDmaAddr = config->trbDmaAddr; + priv->dmaCallback.complete = host_CallbackTransfer; + + priv->dmaController = (void *)(priv + 1); + priv->dmaDrv->dma_init(priv->dmaController, &priv->dmaCfg, &priv->dmaCallback); + priv->dmaDrv->dma_setParentPriv(priv->dmaController, priv); + + INIT_LIST_HEAD(&priv->ctrlHEpQueue); + + for (index = 0; index < MAX_INSTANCE_EP_NUM; index++) { + INIT_LIST_HEAD(&priv->isoInHEpQueue[index]); + INIT_LIST_HEAD(&priv->isoOutHEpQueue[index]); + INIT_LIST_HEAD(&priv->intInHEpQueue[index]); + INIT_LIST_HEAD(&priv->intOutHEpQueue[index]); + INIT_LIST_HEAD(&priv->bulkInHEpQueue[index]); + INIT_LIST_HEAD(&priv->bulkOutHEpQueue[index]); + } + + phytium_write8(&priv->regs->cpuctrl, BIT(1)); + phytium_write8(&priv->regs->otgctrl, OTGCTRL_ABUSDROP); + phytium_write8(&priv->regs->ep0maxpack, 0x40); + + //disable interrupts + phytium_write8(&priv->regs->otgien, 0x0); + phytium_write8(&priv->regs->usbien, 0x0); + phytium_write16(&priv->regs->txerrien, 0x0); + phytium_write16(&priv->regs->rxerrien, 0x0); + + //clear all interrupt except otg idle irq + phytium_write8(&priv->regs->otgirq, 0xFE); + phytium_write8(&priv->regs->usbirq, 0xFF); + phytium_write16(&priv->regs->txerrirq, 0xFF); + phytium_write16(&priv->regs->rxerrirq, 0xFF); + + phytium_write8(&priv->regs->tawaitbcon, 0x80); + + initEndpoints(priv); + priv->otgState = HOST_OTG_STATE_B_IDLE; + + //reset all endpoint + phytium_write8(&priv->regs->endprst, ENDPRST_IO_TX); + phytium_write8(&priv->regs->endprst, ENDPRST_FIFORST | ENDPRST_TOGRST | ENDPRST_IO_TX); + phytium_write8(&priv->regs->endprst, ENDPRST_FIFORST | ENDPRST_TOGRST); + + if (isVhubHost) + priv->vhub_regs = (struct VHUB_REGS *)(config->phy_regBase); + else + priv->custom_regs = (struct CUSTOM_REGS *)(config->phy_regBase); + + return 0; +} + +void hostDestroy(struct HOST_CTRL *priv) +{ +} + +void hostStart(struct HOST_CTRL *priv) +{ + uint8_t otgstate, usbien; + + if (!priv) + return; + + priv->dmaDrv->dma_start(priv->dmaController); + usbien = phytium_read8(&priv->regs->usbien); + usbien = usbien | USBIR_URES | USBIR_SUSP; + phytium_write8(&priv->regs->usbien, usbien); +retry: + otgstate = phytium_read8(&priv->regs->otgstate); + switch (otgstate) { + case HOST_OTG_STATE_A_IDLE: + priv->ep0State = HOST_EP0_STAGE_IDLE; + priv->otgState = HOST_OTG_STATE_A_IDLE; + phytium_write8(&priv->regs->otgirq, OTGIRQ_IDLEIRQ); + host_SetVbus(priv, 1); + break; + case HOST_OTG_STATE_B_IDLE: + host_SetVbus(priv, 1); + break; + case HOST_OTG_STATE_A_WAIT_VFALL: + goto retry; + } + + phytium_write8(&priv->regs->otgien, OTGIRQ_CONIRQ | + OTGIRQ_VBUSERRIRQ | OTGIRQ_SRPDETIRQ); +} + +void hostStop(struct HOST_CTRL *priv) +{ + if (!priv) + return; + + phytium_write8(&priv->regs->otgien, 0x0); + phytium_write8(&priv->regs->usbien, 0x0); + phytium_write16(&priv->regs->txerrien, 0x0); + phytium_write16(&priv->regs->rxerrien, 0x0); + + phytium_write8(&priv->regs->otgirq, 0xFE); + phytium_write8(&priv->regs->usbirq, 0xFF); + phytium_write16(&priv->regs->txerrirq, 0xFF); + phytium_write16(&priv->regs->rxerrirq, 0xFF); + + host_SetVbus(priv, 0); + priv->dmaDrv->dma_stop(priv->dmaController); +} + +static void handleReset(struct HOST_CTRL *priv) +{ + if (!priv) + return; + + if (priv->otgState == HOST_OTG_STATE_A_WAIT_BCON + || priv->otgState == HOST_OTG_STATE_B_WAIT_ACON) { + switch (phytium_read8(&priv->regs->speedctrl)) { + case SPEEDCTRL_HS: + priv->portStatus |= USB_PORT_STAT_HIGH_SPEED; + break; + case SPEEDCTRL_FS: + priv->portStatus &= ~(USB_PORT_STAT_HIGH_SPEED | USB_PORT_STAT_LOW_SPEED); + break; + case SPEEDCTRL_LS: + priv->portStatus |= USB_PORT_STAT_LOW_SPEED; + break; + } + + priv->dmaDrv->dma_setHostMode(priv->dmaController); + + if (priv->hostCallbacks.portStatusChange) + priv->hostCallbacks.portStatusChange(priv); + + switch (phytium_read8(&priv->regs->otgstate)) { + case HOST_OTG_STATE_B_HOST: + priv->otgState = HOST_OTG_STATE_B_HOST; + break; + case HOST_OTG_STATE_A_HOST: + break; + default: + priv->otgState = HOST_OTG_STATE_A_HOST; + break; + } + } +} + +void hostIsr(struct HOST_CTRL *priv) +{ + uint8_t usbirq, usbien; + + if (!priv) + return; + + usbirq = phytium_read8(&priv->regs->usbirq); + usbien = phytium_read8(&priv->regs->usbien); + pr_debug("raw usbirq:0x%x usbien:0x%x\n", usbirq, usbien); + usbirq = usbirq & usbien; + + hostErrorIrq(priv); + hostOtgIrq(priv); + + if (!usbirq) + goto DMA_IRQ; + + if (usbirq & USBIR_URES) { + phytium_write8(&priv->regs->usbirq, USBIR_URES); + priv->port_resetting = 0; + handleReset(priv); + } + + if (usbirq & USBIR_SOF) + phytium_write8(&priv->regs->usbirq, USBIR_SOF); + + if (usbirq & USBIR_SUSP) { + pr_debug("clear suspend irq\n"); + phytium_write8(&priv->regs->usbirq, USBIR_SUSP); + phytium_write8(&priv->regs->clkgate, 0x7); + } + + return; +DMA_IRQ: + priv->dmaDrv->dma_isr(priv->dmaController); +} + +int32_t hostEpDisable(struct HOST_CTRL *priv, struct HOST_EP *ep) +{ + return 0; +} + +unsigned int get_endpoint_interval(struct usb_endpoint_descriptor desc, + int speed) +{ + unsigned int interval = 0; + + switch (speed) { + case USB_SPEED_HIGH: + if (usb_endpoint_xfer_control(&desc) || usb_endpoint_xfer_bulk(&desc)) { + if (desc.bInterval == 0) + return interval; + interval = fls(desc.bInterval) - 1; + interval = clamp_val(interval, 0, 15); + interval = 1 << interval; + if (interval != desc.bInterval) + pr_debug("rounding to %d microframes, desc %d microframes\n", + interval, desc.bInterval); + break; + } + + if (usb_endpoint_xfer_isoc(&desc) || usb_endpoint_xfer_int(&desc)) { + interval = clamp_val(desc.bInterval, 1, 16) - 1; + interval = 1 << interval; + if (interval != desc.bInterval - 1) + pr_debug("rounding to %d %sframes\n", interval, + speed == USB_SPEED_FULL ? "" : "micro"); + } + break; + case USB_SPEED_FULL: + if (usb_endpoint_xfer_isoc(&desc)) { + interval = clamp_val(desc.bInterval, 1, 16) - 1; + if (interval != desc.bInterval - 1) + pr_debug("rounding to %d %sframes\n", 1 << interval, + speed == USB_SPEED_FULL ? "" : "micro"); + interval += 3; + break; + } + /* fall through */ + case USB_SPEED_LOW: + if (usb_endpoint_xfer_int(&desc) || usb_endpoint_xfer_isoc(&desc)) { + interval = fls(desc.bInterval * 8) - 1; + interval = clamp_val(interval, 3, 10); + if ((1 << interval) != desc.bInterval * 8) + pr_debug("rounding to %d microframes, desc %d microframes\n", + 1 << interval, desc.bInterval); + } + } + + return interval; +} + +int32_t hostReqQueue(struct HOST_CTRL *priv, struct HOST_REQ *req) +{ + struct HOST_EP_PRIV *hostEpPriv; + struct list_head *hEpQueue = NULL; + uint8_t idleQueue = 0; + + if (!priv || !req) + return -EINVAL; + + list_add_tail((struct list_head *)&req->list, (struct list_head *)&req->usbEp->reqList); + hostEpPriv = (struct HOST_EP_PRIV *)req->usbEp->hcPriv; + + if (hostEpPriv->epIsReady) { + if (usb_endpoint_xfer_isoc(&req->usbEp->desc)) { + hostEpPriv->isocEpConfigured = 1; + hostStartReq(priv, req); + } + return 0; + } + + hostEpPriv->epIsReady = 1; + hostEpPriv->maxPacketSize = req->usbEp->desc.wMaxPacketSize; + hostEpPriv->interval = req->interval; + hostEpPriv->epNum = req->usbEp->desc.bEndpointAddress & 0xf; + hostEpPriv->faddress = req->faddress; + hostEpPriv->usbHEp = req->usbEp; + hostEpPriv->isIn = req->epIsIn; + + hostEpPriv->frame = get_endpoint_interval(req->usbEp->desc, req->usbDev->speed); + hostEpPriv->interval = hostEpPriv->frame; + switch (usb_endpoint_type(&req->usbEp->desc)) { + case USB_ENDPOINT_XFER_CONTROL: + hostEpPriv->isIn = 0; + hEpQueue = &priv->ctrlHEpQueue; + hostEpPriv->type = USB_ENDPOINT_XFER_CONTROL; + break; + case USB_ENDPOINT_XFER_BULK: + hEpQueue = hostEpPriv->isIn ? &priv->bulkInHEpQueue[req->epNum - 1] : + &priv->bulkOutHEpQueue[req->epNum - 1]; + hostEpPriv->type = USB_ENDPOINT_XFER_BULK; + break; + case USB_ENDPOINT_XFER_INT: + hEpQueue = hostEpPriv->isIn ? &priv->intInHEpQueue[req->epNum - 1] : + &priv->intOutHEpQueue[req->epNum - 1]; + hostEpPriv->type = USB_ENDPOINT_XFER_INT; + break; + case USB_ENDPOINT_XFER_ISOC: + hEpQueue = hostEpPriv->isIn ? &priv->isoInHEpQueue[req->epNum - 1] : + &priv->isoOutHEpQueue[req->epNum - 1]; + hostEpPriv->type = USB_ENDPOINT_XFER_ISOC; + break; + default: + break; + } + + if (hostEpPriv->isIn) + hostEpPriv->genericHwEp = &priv->in[req->epNum]; + else + hostEpPriv->genericHwEp = &priv->out[req->epNum]; + + hostEpPriv->genericHwEp->state = HOST_EP_ALLOCATED; + hostEpPriv->genericHwEp->refCount++; + + if (list_empty(hEpQueue)) { + if (hostEpPriv->genericHwEp->state == HOST_EP_BUSY) { + pr_err("Error:Hardware endpoint %d is busy\n", + hostEpPriv->genericHwEp->hwEpNum); + return -EINVAL; + } + idleQueue = 1; + } + + if (usb_endpoint_xfer_int(&req->usbEp->desc)) + list_add_tail(&hostEpPriv->node, hEpQueue); + + if (idleQueue) + hostStartReq(priv, req); + + return 0; +} + +static int abortActuallyUsbRequest(struct HOST_CTRL *priv, + struct HOST_REQ *req, struct HOST_EP *usbEp) +{ + struct HOST_EP_PRIV *usbEpPriv; + struct HostEp *hostEp; + uint16_t rxerrien = 0; + uint16_t txerrien = 0; + uint8_t rxcon, txcon; + + if (!priv || !req || !usbEp) + return -EINVAL; + + usbEpPriv = (struct HOST_EP_PRIV *)usbEp->hcPriv; + hostEp = usbEpPriv->currentHwEp; + + usbEpPriv->transferFinished = 1; + + if (hostEp->isInEp) { + if (hostEp->hwEpNum) { + rxcon = phytium_read8(&priv->regs->ep[hostEp->hwEpNum - 1].rxcon); + rxcon = rxcon & (~BIT(7)); + phytium_write8(&priv->regs->ep[hostEp->hwEpNum - 1].rxcon, rxcon); + } + rxerrien = phytium_read16(&priv->regs->rxerrien); + rxerrien &= ~(1 << hostEp->hwEpNum); + phytium_write16(&priv->regs->rxerrien, rxerrien); + phytium_write8(&priv->regs->endprst, ENDPRST_FIFORST | + ENDPRST_IO_TX | hostEp->hwEpNum); + } else { + if (hostEp->hwEpNum) { + txcon = phytium_read8(&priv->regs->ep[hostEp->hwEpNum - 1].txcon); + txcon = txcon & (~BIT(7)); + phytium_write8(&priv->regs->ep[hostEp->hwEpNum - 1].txcon, txcon); + } + txerrien = phytium_read16(&priv->regs->txerrien); + txerrien &= ~(1 << hostEp->hwEpNum); + phytium_write16(&priv->regs->txerrien, txerrien); + phytium_write8(&priv->regs->endprst, ENDPRST_FIFORST | hostEp->hwEpNum); + } + + scheduleNextTransfer(priv, req, hostEp); + + return 0; +} + +int32_t hostReqDequeue(struct HOST_CTRL *priv, struct HOST_REQ *req, uint32_t status) +{ + struct HOST_EP *usbEp; + struct HOST_EP_PRIV *usbEpPriv; + int ret = 0; + + if (!priv || !req) + return -EINVAL; + + usbEp = req->usbEp; + usbEpPriv = (struct HOST_EP_PRIV *)usbEp->hcPriv; + + pr_debug("Dequeue usbReq:%p dev%d usbEp%d%s\n", req, req->faddress, req->epNum, + req->epIsIn ? "in" : "out"); + + if (!usbEpPriv->epIsReady) + return 0; + + if (!usbEpPriv->currentHwEp || req->list.prev != &usbEp->reqList) { + givebackRequest(priv, req, status); + if (list_empty(&usbEp->reqList)) { + usbEpPriv->epIsReady = 0; + usbEpPriv->currentHwEp = NULL; + if (usb_endpoint_xfer_int(&usbEp->desc)) + list_del(&usbEpPriv->node); + } + } else + ret = abortActuallyUsbRequest(priv, req, usbEp); + + if (usbEpPriv->isocEpConfigured) + usbEpPriv->isocEpConfigured = 0; + + return ret; +} + +int32_t hostVHubStatusData(struct HOST_CTRL *priv, uint8_t *status) +{ + return 0; +} + +int32_t hostGetDevicePD(struct HOST_CTRL *priv) +{ + return 0; +} + +int32_t hostGetPrivateDataSize(struct HOST_CTRL *priv) +{ + if (!priv) + return 0; + + return sizeof(struct HOST_EP_PRIV); +} + +static int hub_descriptor(struct usb_hub_descriptor *buf) +{ + buf->bDescLength = 9; + buf->bDescriptorType = 0x29; + buf->bNbrPorts = 1; + buf->wHubCharacteristics = 0x11; + buf->bPwrOn2PwrGood = 5; + buf->bHubContrCurrent = 0; + buf->u.hs.DeviceRemovable[0] = 0x2; + + return 0; +} + +static int HubPortSuspend(struct HOST_CTRL *priv, u16 on) +{ + uint8_t otgctrl; + + if (!priv) + return 0; + + otgctrl = phytium_read8(&priv->regs->otgctrl); + + if (on) { + otgctrl &= ~OTGCTRL_BUSREQ; + otgctrl &= ~OTGCTRL_BHNPEN; + + priv->portStatus |= USB_PORT_STAT_SUSPEND; + + switch (phytium_read8(&priv->regs->otgstate)) { + case HOST_OTG_STATE_A_HOST: + priv->otgState = HOST_OTG_STATE_A_SUSPEND; + otgctrl |= OTGCTRL_ASETBHNPEN; + break; + case HOST_OTG_STATE_B_HOST: + priv->otgState = HOST_OTG_STATE_B_HOST_2; + break; + default: + break; + } + phytium_write8(&priv->regs->otgctrl, otgctrl); + } else { + otgctrl |= OTGCTRL_BUSREQ; + otgctrl &= ~OTGCTRL_ASETBHNPEN; + phytium_write8(&priv->regs->otgctrl, otgctrl); + priv->portStatus |= USB_PORT_STAT_RESUME; + } + return 0; +} + +static void HubPortReset(struct HOST_CTRL *priv, uint8_t on) +{ + uint8_t speed; + + if (!priv) + return; + + if (on) { + phytium_write16(&priv->regs->txerrirq, 0xFFFF); + phytium_write16(&priv->regs->txirq, 0xFFFF); + phytium_write16(&priv->regs->rxerrirq, 0xFFFF); + phytium_write16(&priv->regs->rxirq, 0xFFFF); + + phytium_write8(&priv->regs->endprst, ENDPRST_IO_TX); + phytium_write8(&priv->regs->endprst, ENDPRST_FIFORST | + ENDPRST_TOGRST | ENDPRST_IO_TX); + phytium_write8(&priv->regs->endprst, ENDPRST_FIFORST | ENDPRST_TOGRST); + phytium_write8(&priv->regs->ep0fifoctrl, FIFOCTRL_FIFOAUTO | 0 | 0x04); + phytium_write8(&priv->regs->ep0fifoctrl, FIFOCTRL_FIFOAUTO | + FIFOCTRL_IO_TX | 0 | 0x04); + + priv->portStatus |= USB_PORT_STAT_RESET; + priv->portStatus &= ~USB_PORT_STAT_ENABLE; + priv->port_resetting = 0; + } else { + speed = phytium_read8(&priv->regs->speedctrl); + if (speed == SPEEDCTRL_HS) + priv->portStatus |= USB_PORT_STAT_HIGH_SPEED; + else if (speed == SPEEDCTRL_FS) + priv->portStatus &= ~(USB_PORT_STAT_HIGH_SPEED | USB_PORT_STAT_LOW_SPEED); + else + priv->portStatus |= USB_PORT_STAT_LOW_SPEED; + + priv->portStatus &= ~USB_PORT_STAT_RESET; + priv->portStatus |= USB_PORT_STAT_ENABLE | (USB_PORT_STAT_C_RESET << 16) + | (USB_PORT_STAT_C_ENABLE << 16); + + if (priv->hostCallbacks.portStatusChange) + priv->hostCallbacks.portStatusChange(priv); + } +} + +static int get_PortStatus(struct HOST_CTRL *priv, u16 wIndex, uint8_t *buff) +{ + uint32_t temp = 0; + + if (!priv || !buff) + return 0; + + if ((wIndex & 0xff) != 1) + return 1; + + if (priv->portStatus & USB_PORT_STAT_RESET) { + if (!priv->port_resetting) + HubPortReset(priv, 0); + } + + if (priv->portStatus & USB_PORT_STAT_RESUME) { + priv->portStatus &= ~(USB_PORT_STAT_SUSPEND | USB_PORT_STAT_RESUME); + priv->portStatus |= USB_PORT_STAT_C_SUSPEND << 16; + if (priv->hostCallbacks.portStatusChange) + priv->hostCallbacks.portStatusChange(priv); + } + + temp = priv->portStatus & (~USB_PORT_STAT_RESUME); + buff[0] = temp; + buff[1] = temp >> 8; + buff[2] = temp >> 16; + buff[3] = temp >> 24; + + return 0; +} + +static int set_PortFeature(struct HOST_CTRL *priv, u16 wValue, u16 wIndex) +{ +// struct HW_Regs *regs = priv->regs; + + if ((wIndex & 0xff) != 1) + return 1; + + switch (wValue) { + case USB_PORT_FEAT_CONNECTION: + break; + case USB_PORT_FEAT_ENABLE: + break; + case USB_PORT_FEAT_SUSPEND: + HubPortSuspend(priv, 1); + break; + case USB_PORT_FEAT_OVER_CURRENT: + break; + case USB_PORT_FEAT_RESET: + HubPortReset(priv, 1); + break; + case USB_PORT_FEAT_L1: + break; + case USB_PORT_FEAT_POWER: + hostStart(priv); + break; + case USB_PORT_FEAT_LOWSPEED: + break; + case USB_PORT_FEAT_C_CONNECTION: + break; + case USB_PORT_FEAT_C_ENABLE: + break; + case USB_PORT_FEAT_C_SUSPEND: + break; + case USB_PORT_FEAT_C_OVER_CURRENT: + break; + case USB_PORT_FEAT_INDICATOR: + break; + case USB_PORT_FEAT_C_PORT_L1: + break; + default: + break; + } + priv->portStatus |= 1 << wValue; + + return 0; +} + +static int Clear_PortFeature(struct HOST_CTRL *priv, u16 wValue, u16 wIndex) +{ + if ((wIndex & 0xff) != 1) + return 1; + + switch (wValue) { + case USB_PORT_FEAT_CONNECTION: + break; + case USB_PORT_FEAT_ENABLE: + break; + case USB_PORT_FEAT_SUSPEND: + HubPortSuspend(priv, 0); + break; + case USB_PORT_FEAT_OVER_CURRENT: + break; + case USB_PORT_FEAT_RESET: + break; + case USB_PORT_FEAT_L1: + break; + case USB_PORT_FEAT_POWER: + break; + case USB_PORT_FEAT_LOWSPEED: + break; + case USB_PORT_FEAT_C_CONNECTION: + break; + case USB_PORT_FEAT_C_ENABLE: + break; + case USB_PORT_FEAT_C_SUSPEND: + break; + case USB_PORT_FEAT_C_OVER_CURRENT: + break; + case USB_PORT_FEAT_INDICATOR: + break; + case USB_PORT_FEAT_C_PORT_L1: + break; + default: + break; + } + priv->portStatus &= ~(1 << wValue); + + return 0; +} + +static int hc_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, + u16 wIndex, char *buf, u16 wLength) +{ + unsigned long flags = 0; + int retval = 0; + struct HOST_CTRL *priv; + struct phytium_cusb *config = *(struct phytium_cusb **)hcd->hcd_priv; + + if (!config) + return -EINVAL; + + if (!buf) + return -EINVAL; + + if (unlikely(!HCD_HW_ACCESSIBLE(hcd))) { + spin_unlock_irqrestore(&config->lock, flags); + return -ESHUTDOWN; + } + + priv = (struct HOST_CTRL *)config->host_priv; + if (!priv) + return -EINVAL; + + spin_lock_irqsave(&config->lock, flags); + switch (typeReq) { + case GetHubStatus: + break; + case GetPortStatus: + get_PortStatus(priv, wIndex, (uint8_t *)buf); + break; + case GetHubDescriptor: + hub_descriptor((struct usb_hub_descriptor *)buf); + break; + case SetPortFeature: + set_PortFeature(priv, wValue, wIndex); + break; + case ClearPortFeature: + retval = Clear_PortFeature(priv, wValue, wIndex); + break; + case SetHubFeature: + break; + case ClearHubFeature: + break; + default: + break; + } + + spin_unlock_irqrestore(&config->lock, flags); + + return retval; +} + +int32_t hostVHubControl(struct HOST_CTRL *priv, struct usb_ctrlrequest *setup, uint8_t *buff) +{ + uint16_t request; + uint32_t retval = 0; + + if (!priv || !setup || !buff) + return -EINVAL; + + request = setup->bRequestType << 0x08 | setup->bRequest; + switch (request) { + case ClearHubFeature: + break; + case SetHubFeature: + break; + case ClearPortFeature: + retval = Clear_PortFeature(priv, setup->wValue, setup->wIndex); + break; + case SetPortFeature: + retval = set_PortFeature(priv, setup->wValue, setup->wIndex); + break; + case GetHubDescriptor: + retval = hub_descriptor((struct usb_hub_descriptor *)buff); + break; + case GetHubStatus: + break; + case GetPortStatus: + retval = get_PortStatus(priv, setup->wIndex, (uint8_t *)buff); + break; + default: + retval = EOPNOTSUPP; + break; + } + + return retval; +} + +struct HOST_OBJ hostDrv = { + .host_init = hostInit, + .host_destroy = hostDestroy, + .host_start = hostStart, + .host_stop = hostStop, + .host_isr = hostIsr, + + //endpoint operation + .host_epDisable = hostEpDisable, + .host_reqQueue = hostReqQueue, + .host_reqDequeue = hostReqDequeue, + .host_vhubStatusData = hostVHubStatusData, + .host_vhubControl = hostVHubControl, + .host_getDevicePD = hostGetDevicePD, + .host_epGetPrivateDataSize = hostGetPrivateDataSize, +}; + +static struct hc_driver host_driver = { + .description = "phytium-hcd", + .product_desc = "Phytium Host USB Driver", + .hcd_priv_size = sizeof(struct phytium_cusb *), + .flags = HCD_MEMORY | HCD_USB2 | HCD_DMA, + .reset = hc_reset, + .start = hc_start, + .stop = hc_stop, + .shutdown = hc_shutdown, + .urb_enqueue = hc_urb_enqueue, + .urb_dequeue = hc_urb_dequeue, + .endpoint_disable = hc_endpoint_disable, + .get_frame_number = hc_get_frame, + .alloc_dev = hc_alloc_dev, + .free_dev = hc_free_dev, + .reset_device = hc_reset_device, + .update_device = hc_update_device, + .add_endpoint = hc_add_endpoint, + .drop_endpoint = hc_drop_endpoint, + .hub_status_data = hc_hub_status_data, + .hub_control = hc_hub_control, +#ifdef CONFIG_PM + .bus_suspend = hc_bus_suspend, + .bus_resume = hc_bus_resume, +#endif +}; + +static int phytium_host_set_default_cfg(struct phytium_cusb *config) +{ + int index; + + config->host_cfg.regBase = (uintptr_t)config->regs; + config->host_cfg.phy_regBase = (uintptr_t)config->phy_regs; + config->host_cfg.dmultEnabled = 1; + config->host_cfg.memoryAlignment = 0; + config->host_cfg.dmaSupport = 1; + config->host_cfg.isEmbeddedHost = 1; + + for (index = 0; index < HOST_EP_NUM; index++) { + if (index == 0) { + config->host_cfg.epIN[index].bufferingValue = 1; + config->host_cfg.epIN[index].maxPacketSize = 64; + config->host_cfg.epIN[index].startBuf = 0; + + config->host_cfg.epOUT[index].bufferingValue = 1; + config->host_cfg.epOUT[index].maxPacketSize = 64; + config->host_cfg.epOUT[index].startBuf = 0; + } else { + config->host_cfg.epIN[index].bufferingValue = 2; + config->host_cfg.epIN[index].maxPacketSize = 1024; + config->host_cfg.epIN[index].startBuf = 64 + 2 * 1024 * (index - 1); + + config->host_cfg.epOUT[index].bufferingValue = 2; + config->host_cfg.epOUT[index].maxPacketSize = 1024; + config->host_cfg.epOUT[index].startBuf = 64 + 2 * 1024 * (index - 1); + } + } + + return 0; +} + +static int phytium_host_reinit(struct phytium_cusb *config) +{ + struct HOST_CTRL *ctrl; + + if (!config || !config->host_priv) + return 0; + + ctrl = (struct HOST_CTRL *)config->host_priv; + + usb_root_hub_lost_power(config->hcd->self.root_hub); + hostStop(ctrl); + + ctrl->portStatus = 0; + + config->host_obj->host_init(config->host_priv, &config->host_cfg, + &config->host_callbacks, config->dev, config->isVhubHost); + + return 0; +} + +int phytium_host_init(struct phytium_cusb *config) +{ + int ret; + + if (!config) + return 0; + + phytium_host_set_default_cfg(config); + config->host_obj = HOST_GetInstance(); + config->dma_cfg.regBase = config->host_cfg.regBase + 0x400; + + config->dma_obj = DMA_GetInstance(); + config->dma_obj->dma_probe(&config->dma_cfg, &config->dma_sysreq); + + config->host_sysreq.privDataSize = sizeof(struct HOST_CTRL); + config->host_sysreq.trbMemSize = config->dma_sysreq.trbMemSize; + config->host_sysreq.privDataSize += config->dma_sysreq.privDataSize; + + config->host_priv = devm_kzalloc(config->dev, config->host_sysreq.privDataSize, GFP_KERNEL); + if (!config->host_priv) { + ret = -ENOMEM; + goto err_probe; + } + + config->host_cfg.trbAddr = dma_alloc_coherent(config->dev, config->host_sysreq.trbMemSize, + (dma_addr_t *)&config->host_cfg.trbDmaAddr, GFP_KERNEL); + if (!config->host_cfg.trbAddr) { + ret = -ENOMEM; + goto err_dma_coherent; + } + + config->host_callbacks.portStatusChange = host_rh_port_status_change; + config->host_callbacks.getEpToggle = host_get_ep_toggle; + config->host_callbacks.setEpToggle = host_set_ep_toggle; + config->host_callbacks.givebackRequest = host_giveback_request; + + config->host_obj->host_init(config->host_priv, &config->host_cfg, + &config->host_callbacks, config->dev, config->isVhubHost); + + config->hcd = usb_create_hcd(&host_driver, config->dev, dev_name(config->dev)); + if (!config->hcd) { + ret = -ENODEV; + goto err_host; + } + + *config->hcd->hcd_priv = (unsigned long)config; + config->hcd->self.uses_pio_for_control = 0; + config->hcd->uses_new_polling = 1; + config->hcd->has_tt = 1; + + dev_set_drvdata(config->dev, config); + + config->hcd->self.otg_port = 1; + config->hcd->power_budget = 500; + ret = usb_add_hcd(config->hcd, 0, 0); + if (ret < 0) + goto err_setup; + + return 0; +err_setup: + usb_put_hcd(config->hcd); +err_host: + config->host_obj->host_destroy(config->host_priv); + dma_free_coherent(config->dev, config->host_sysreq.trbMemSize, + config->host_cfg.trbAddr, config->host_cfg.trbDmaAddr); +err_dma_coherent: +err_probe: + dev_set_drvdata(config->dev, NULL); + return ret; +} + +int phytium_host_uninit(struct phytium_cusb *config) +{ + if (!config) + return 0; + + if (config->hcd) { + usb_remove_hcd(config->hcd); + usb_put_hcd(config->hcd); + config->hcd = NULL; + } + + return 0; +} + +#ifdef CONFIG_PM +int phytium_host_resume(void *priv) +{ + int otgctrl; + struct phytium_cusb *config = (struct phytium_cusb *)priv; + struct HOST_CTRL *ctrl = (struct HOST_CTRL *)config->host_priv; + + if (!ctrl) + return -EINVAL; + + otgctrl = phytium_read8(&ctrl->regs->otgctrl); + otgctrl |= 1; + phytium_write8(&ctrl->regs->otgctrl, otgctrl); + + phytium_host_reinit(config); + + return 0; +} + +int phytium_host_suspend(void *priv) +{ + int otgctrl; + struct phytium_cusb *config = (struct phytium_cusb *)priv; + struct HOST_CTRL *ctrl = (struct HOST_CTRL *)config->host_priv; + + if (!ctrl) + return -EINVAL; + + otgctrl = phytium_read8(&ctrl->regs->otgctrl); + otgctrl = otgctrl & (~1); + phytium_write8(&ctrl->regs->otgctrl, otgctrl); + + return 0; +} +#endif + +struct HOST_OBJ *HOST_GetInstance(void) +{ + return &hostDrv; +} diff --git a/target/linux/phytium/files-5.10/drivers/usb/phytium/host_api.h b/target/linux/phytium/files-5.10/drivers/usb/phytium/host_api.h new file mode 100644 index 000000000..3d4525827 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/usb/phytium/host_api.h @@ -0,0 +1,248 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __PHYTIUM_HOST_API_H_ +#define __PHYTIUM_HOST_API_H_ + +#include +//#include "list.h" +#include "dma.h" + +#define MAX_SUPPORTED_DEVICES 16 +#define USB_PORT_STAT_RESUME (1 << 31) +#define MAX_INSTANCE_EP_NUM 6 + +enum HOST_OtgState { + HOST_OTG_STATE_A_IDLE, + HOST_OTG_STATE_A_WAIT_VRISE, + HOST_OTG_STATE_A_WAIT_BCON, + HOST_OTG_STATE_A_HOST, + HOST_OTG_STATE_A_SUSPEND, + HOST_OTG_STATE_A_PERIPHERAL, + HOST_OTG_STATE_A_VBUS_ERR, + HOST_OTG_STATE_A_WAIT_VFALL, + HOST_OTG_STATE_B_IDLE = 0x10, + HOST_OTG_STATE_B_PERIPHERAL, + HOST_OTG_STATE_B_WAIT_ACON, + HOST_OTG_STATE_B_HOST, + HOST_OTG_STATE_B_HOST_2, + HOST_OTG_STATE_B_SRP_INT1, + HOST_OTG_STATE_B_SRP_INT2, + HOST_OTG_STATE_B_DISCHRG1, + HOST_OTG_STATE_B_DISCHRG2, + HOST_OTG_STATE_UNKNOWN, +}; + +enum HOST_EP0_STAGE { + HOST_EP0_STAGE_IDLE, + HOST_EP0_STAGE_SETUP, + HOST_EP0_STAGE_IN, + HOST_EP0_STAGE_OUT, + HOST_EP0_STAGE_STATUSIN, + HOST_EP0_STAGE_STATUSOUT, + HOST_EP0_STAGE_ACK, +}; + +enum HOST_EP_STATE { + HOST_EP_FREE, + HOST_EP_ALLOCATED, + HOST_EP_BUSY, + HOST_EP_NOT_IMPLEMENTED +}; + +struct HOST_DEVICE { + uint8_t devnum; + uint8_t hubPort; + unsigned int speed; + struct HOST_DEVICE *parent; + void *hcPriv; + void *userExt; +}; + +struct HOST_EP { + struct usb_endpoint_descriptor desc; + struct list_head reqList; + void *userExt; + uint8_t *hcPriv; +}; + +struct HOST_USB_DEVICE { + struct HOST_EP ep0_hep; + struct HOST_EP *in_ep[16]; + struct HOST_EP *out_ep[16]; + struct HOST_DEVICE udev; + struct usb_device *ld_udev; +}; + +struct HostEp { + uint8_t name[255]; + uint8_t hwEpNum; + uint8_t hwBuffers; + uint16_t hwMaxPacketSize; + uint8_t isInEp; + void *channel; + uint8_t usbEpNum; + uint8_t type; + uint8_t usbPacketSize; + enum HOST_EP_STATE state; + struct HOST_EP *scheduledUsbHEp; + uint8_t refCount; +}; + +struct HOST_ISOFRAMESDESC { + uint32_t length; + uint32_t offset; +}; + +struct HOST_EP_PRIV { + struct list_head node; + struct HostEp *genericHwEp; + struct HostEp *currentHwEp; + uint8_t epIsReady; + uint8_t isIn; + uint8_t type; + uint8_t interval; + uint8_t epNum; + uint8_t faddress; + uint16_t maxPacketSize; + uint32_t frame; + uint8_t hubAddress; + uint8_t portAddress; + uint8_t split; + struct HOST_EP *usbHEp; + uint8_t isocEpConfigured; + uint8_t transferFinished; +}; + +struct HOST_REQ { + struct list_head list; + struct HOST_EP *usbEp; + void *userExt; + void *hcPriv; + struct HOST_DEVICE *usbDev; + struct usb_ctrlrequest *setup; + uintptr_t setupDma; + void *bufAddress; + uintptr_t buffDma; + uint32_t buffLength; + uint32_t actualLength; + uint8_t epIsIn; + uint8_t eptype; + uint8_t epNum; + uint8_t faddress; + uint8_t interval; + uint8_t status; + uint8_t reqUnlinked; + struct HOST_ISOFRAMESDESC *isoFramesDesc; + uint32_t isoFramesNumber; +}; + +struct HOST_SYSREQ { + uint32_t privDataSize; + uint32_t trbMemSize; +}; + +struct HOST_EP_CFG { + uint8_t bufferingValue; + uint16_t startBuf; + uint16_t maxPacketSize; +}; + +struct HOST_CFG { + uintptr_t regBase; + uintptr_t phy_regBase; + struct HOST_EP_CFG epIN[16]; + struct HOST_EP_CFG epOUT[16]; + uint8_t dmultEnabled; + uint8_t memoryAlignment; + uint8_t dmaSupport; + uint8_t isEmbeddedHost; + void *trbAddr; + uintptr_t trbDmaAddr; +}; + +struct HOST_CTRL; + +struct HOST_CALLBACKS { + void (*portStatusChange)(struct HOST_CTRL *priv); + + uint8_t (*getEpToggle)(struct HOST_CTRL *priv, + struct HOST_DEVICE *usbDev, uint8_t epNum, uint8_t isIn); + + void (*setEpToggle)(struct HOST_CTRL *priv, struct HOST_DEVICE *usbDev, + uint8_t epNum, uint8_t isIn, uint8_t toggle); + + void (*givebackRequest)(struct HOST_CTRL *priv, + struct HOST_REQ *usbReq, uint32_t status); + + void (*setTimer)(struct HOST_CTRL *priv, uint32_t time, uint8_t id); +}; + +struct HOST_OBJ { + int32_t (*host_probe)(struct HOST_CFG *config, struct HOST_SYSREQ *sysReq); + + int32_t (*host_init)(struct HOST_CTRL *priv, struct HOST_CFG *config, + struct HOST_CALLBACKS *callbacks, struct device *pdev, bool isVhubHost); + + void (*host_destroy)(struct HOST_CTRL *priv); + + void (*host_start)(struct HOST_CTRL *priv); + + void (*host_stop)(struct HOST_CTRL *priv); + + void (*host_isr)(struct HOST_CTRL *priv); + + int32_t (*host_epDisable)(struct HOST_CTRL *priv, struct HOST_EP *ep); + + int32_t (*host_reqQueue)(struct HOST_CTRL *priv, struct HOST_REQ *req); + + int32_t (*host_reqDequeue)(struct HOST_CTRL *priv, + struct HOST_REQ *req, uint32_t status); + + int32_t (*host_vhubStatusData)(struct HOST_CTRL *priv, uint8_t *status); + + int32_t (*host_vhubControl)(struct HOST_CTRL *priv, + struct usb_ctrlrequest *setup, uint8_t *buff); + + int32_t (*host_getDevicePD)(struct HOST_CTRL *priv); + + int32_t (*host_epGetPrivateDataSize)(struct HOST_CTRL *priv); +}; + +struct HOST_CTRL { + struct device *dev; + struct HW_REGS *regs; + struct HOST_OBJ *hostDrv; + struct HOST_CFG hostCfg; + struct HOST_CALLBACKS hostCallbacks; + struct HostEp in[16]; + struct HostEp out[16]; + uint32_t portStatus; + struct list_head ctrlHEpQueue; + struct list_head isoInHEpQueue[MAX_INSTANCE_EP_NUM]; + struct list_head isoOutHEpQueue[MAX_INSTANCE_EP_NUM]; + struct list_head intInHEpQueue[MAX_INSTANCE_EP_NUM]; + struct list_head intOutHEpQueue[MAX_INSTANCE_EP_NUM]; + struct list_head bulkInHEpQueue[MAX_INSTANCE_EP_NUM]; + struct list_head bulkOutHEpQueue[MAX_INSTANCE_EP_NUM]; + uint8_t hwEpInCount; + uint8_t hwEpOutCount; + unsigned int speed; + enum HOST_OtgState otgState; + enum HOST_EP0_STAGE ep0State; + uint8_t vBusErrCnt; + uint8_t isRemoteWakeup; + uint8_t isSelfPowered; + uint8_t deviceAddress; + struct DMA_OBJ *dmaDrv; + void *dmaController; + struct DMA_CFG dmaCfg; + struct DMA_CALLBACKS dmaCallback; + uint8_t port_resetting; + struct HOST_USB_DEVICE *host_devices_table[MAX_SUPPORTED_DEVICES]; + struct CUSTOM_REGS *custom_regs; + struct VHUB_REGS *vhub_regs; +}; + +struct HOST_OBJ *HOST_GetInstance(void); + +#endif diff --git a/target/linux/phytium/files-5.10/drivers/usb/phytium/hw-regs.h b/target/linux/phytium/files-5.10/drivers/usb/phytium/hw-regs.h new file mode 100644 index 000000000..8da9f8e9b --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/usb/phytium/hw-regs.h @@ -0,0 +1,297 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __LINUX_PHYTIUM_HW_REGS +#define __LINUX_PHYTIUM_HW_REGS + +#define USBIEN 0x198 +#define USBIEN_SUDAVIE BIT(0) +#define USBIEN_SOFIE BIT(1) +#define USBIEN_SUTOKIE BIT(2) +#define USBIEN_SUSPIE BIT(3) +#define USBIEN_URESIE BIT(4) +#define USBIEN_HSPEEDIE BIT(5) +#define USBIEN_LPMIE BIT(7) + +#define USBCS 0x1a3 +#define USBCS_LSMODE BIT(0) +#define USBCS_LPMNYET BIT(1) +#define USBCS_SIGSUME BIT(5) +#define USBCS_DISCON BIT(6) +#define USBCS_WAKESRC BIT(7) + +#define USBIR_SOF BIT(1) +#define USBIR_SUSP BIT(3) +#define USBIR_URES BIT(4) +#define USBIR_HSPEED BIT(5) + +#define USBIR_SUDAV BIT(0) +#define USBIR_SUTOK BIT(2) +#define USBIR_LPMIR BIT(7) + +#define OTGIRQ_IDLEIRQ BIT(0) +#define OTGIRQ_SRPDETIRQ BIT(1) +#define OTGIRQ_CONIRQ BIT(2) +#define OTGIRQ_LOCSOFIRQ BIT(2) +#define OTGIRQ_VBUSERRIRQ BIT(3) +#define OTGIRQ_PERIPHIRQ BIT(4) +#define OTGIRQ_IDCHANGEIRQ BIT(5) +#define OTGIRQ_HOSTDISCON BIT(6) +#define OTGIRQ_BSE0SRPIRQ BIT(7) + +#define OTGCTRL_BUSREQ BIT(0) +#define OTGCTRL_ABUSDROP BIT(1) +#define OTGCTRL_ASETBHNPEN BIT(2) +#define OTGCTRL_BHNPEN BIT(3) +#define OTGCTRL_SRPVBUSDETEN BIT(4) +#define OTGCTRL_SRPDATDETEN BIT(5) +#define OTGCTRL_FORCEBCONN BIT(7) + +#define OTGSTATUS_ID BIT(6) + +#define ENDPRST_EP 0x0f +#define ENDPRST_IO_TX BIT(4) +#define ENDPRST_TOGRST BIT(5) +#define ENDPRST_FIFORST BIT(6) +#define ENDPRST_TOGSETQ BIT(7) + +#define FIFOCTRL_EP 0x0f +#define FIFOCTRL_IO_TX BIT(4) +#define FIFOCTRL_FIFOAUTO BIT(5) + +#define SPEEDCTRL_LS BIT(0) +#define SPEEDCTRL_FS BIT(1) +#define SPEEDCTRL_HS BIT(2) +#define SPEEDCTRL_HSDISABLE BIT(7) + +#define CON_TYPE_CONTROL 0x00 +#define CON_TYPE_ISOC 0x04 +#define CON_TYPE_BULK 0x08 +#define CON_TYPE_INT 0x0C +#define CON_TYPE_ISOC_1_ISOD 0x00 +#define CON_TYPE_ISOC_2_ISOD 0x10 +#define CON_TYPE_ISOC_3_ISOD 0x20 +#define CON_STALL 0x40 +#define CON_VAL 0x80 + +#define ERR_TYPE 0x1c +#define ERR_COUNT 0x03 +#define ERR_RESEND BIT(6) +#define ERR_UNDERRIEN BIT(7) + +#define ERR_NONE 0 +#define ERR_CRC 1 +#define ERR_DATA_TOGGLE_MISMATCH 2 +#define ERR_STALL 3 +#define ERR_TIMEOUT 4 +#define ERR_PID 5 +#define ERR_TOO_LONG_PACKET 6 +#define ERR_DATA_UNDERRUN 7 + +#define EP0CS_HCSETTOGGLE BIT(6) +#define EP0CS_HCSET BIT(4) +#define EP0CS_RXBUSY_MASK BIT(3) +#define EP0CS_TXBUSY_MASK BIT(2) +#define EP0CS_STALL BIT(0) +#define EP0CS_HSNAK BIT(1) +#define EP0CS_DSTALL BIT(4) +#define EP0CS_CHGSET BIT(7) + +#define CS_ERR 0x01 +#define CS_BUSY 0x02 +#define CS_NPAK 0x0c +#define CS_NPAK_OFFSET 0x02 +#define CS_AUTO 0x10 + +#define CON_BUF_SINGLE 0x00 +#define CON_BUF_DOUBLE 0x01 +#define CON_BUF_TRIPLE 0x02 +#define CON_BUF_QUAD 0x03 +#define CON_BUF 0x03 + +struct HW_REGS { + uint8_t ep0Rxbc; /*address 0x00*/ + uint8_t ep0Txbc; /*address 0x01*/ + uint8_t ep0cs; /*address 0x02*/ + int8_t reserved0; /*address 0x03*/ + uint8_t lpmctrll; /*address 0x04*/ + uint8_t lpmctrlh; /*address 0x05*/ + uint8_t lpmclock; + uint8_t ep0fifoctrl; + struct ep { /*address 0x08*/ + uint16_t rxbc; //outbc (hcinbc) + uint8_t rxcon; + uint8_t rxcs; + uint16_t txbc; //inbc (hcoutbc + uint8_t txcon; + uint8_t txcs; + } ep[15]; + uint8_t reserved1[4]; + uint32_t fifodat[15]; /*address 0x84*/ + uint8_t ep0ctrl; /*address 0xC0*/ + uint8_t tx0err; /*address 0xC1*/ + uint8_t reserved2; + uint8_t rx0err; /*address 0xC3*/ + struct epExt { + uint8_t txctrl; + uint8_t txerr; + uint8_t rxctrl; + uint8_t rxerr; + } epExt[15]; + uint8_t ep0datatx[64]; /*address 0x100*/ + uint8_t ep0datarx[64]; /*address 0x140*/ + uint8_t setupdat[8]; /*address 0x180*/ + uint16_t txirq; /*address 0x188*/ + uint16_t rxirq; /*address 0x18A*/ + uint8_t usbirq; /*address 0x18C*/ + uint8_t reserved4; + uint16_t rxpngirq; /*address 0x18E*/ + uint16_t txfullirq; /*address 0x190*/ + uint16_t rxemptirq; /*address 0x192*/ + uint16_t txien; /*address 0x194*/ + uint16_t rxien; /*address 0x196*/ + uint8_t usbien; /*address 0x198*/ + uint8_t reserved6; + uint16_t rxpngien; /*address 0x19A*/ + uint16_t txfullien; /*address 0x19C*/ + uint16_t rxemptien; /*address 0x19E*/ + uint8_t usbivect; /*address 0x1A0*/ + uint8_t fifoivect; /*address 0x1A1*/ + uint8_t endprst; /*address 0x1A2*/ + uint8_t usbcs; /*address 0x1A3*/ + uint16_t frmnr; /*address 0x1A4*/ + uint8_t fnaddr; /*address 0x1A6*/ + uint8_t clkgate; /*address 0x1A7*/ + uint8_t fifoctrl; /*address 0x1A8*/ + uint8_t speedctrl; /*address 0x1A9*/ + uint8_t reserved8[1]; /*address 0x1AA*/ + uint8_t portctrl; /*address 0x1AB*/ + uint16_t hcfrmnr; /*address 0x1AC*/ + uint16_t hcfrmremain; /*address 0x1AE*/ + uint8_t reserved9[4]; /*address 0x1B0*/ + uint16_t rxerrirq; /*address 0x1B4*/ + uint16_t txerrirq; /*address 0x1B6*/ + uint16_t rxerrien; /*address 0x1B8*/ + uint16_t txerrien; /*address 0x1BA*/ + /*OTG extension*/ + uint8_t otgirq; /*address 0x1BC*/ + uint8_t otgstate; /*address 0x1BD*/ + uint8_t otgctrl; /*address 0x1BE*/ + uint8_t otgstatus; /*address 0x1BF*/ + uint8_t otgien; /*address 0x1C0*/ + uint8_t taaidlbdis; /*address 0x1C1*/ + uint8_t tawaitbcon; /*address 0x1C2*/ + uint8_t tbvbuspls; /*address 0x1C3*/ + uint8_t otg2ctrl; /*address 0x1C4*/ + uint8_t reserved10[2]; /*address 0x1C5*/ + uint8_t tbvbusdispls; /*address 0x1C7*/ + uint8_t traddr; /*address 0x1C8*/ + uint8_t trwdata; /*address 0x1C9*/ + uint8_t trrdata; /*address 0x1CA*/ + uint8_t trctrl; /*address 0x1CB*/ + uint16_t isoautoarm; /*address 0x1CC*/ + uint8_t adpbc1ien; /*address 0x1CE*/ + uint8_t adpbc2ien; /*address 0x1CF*/ + uint8_t adpbcctr0; /*address 0x1D0*/ + uint8_t adpbcctr1; /*address 0x1D1*/ + uint8_t adpbcctr2; /*address 0x1D2*/ + uint8_t adpbc1irq; /*address 0x1D3*/ + uint8_t adpbc0status; /*address 0x1D4*/ + uint8_t adpbc1status; /*address 0x1D5*/ + uint8_t adpbc2status; /*address 0x1D6*/ + uint8_t adpbc2irq; /*address 0x1D7*/ + uint16_t isodctrl; /*address 0x1D8*/ + uint8_t reserved11[2]; + uint16_t isoautodump; /*address 0x1DC*/ + uint8_t reserved12[2]; + uint8_t ep0maxpack; /*address 0x1E0*/ + uint8_t reserved13; + uint16_t rxmaxpack[15]; /*address 0x1E2*/ + struct rxsoftimer { /*address 0x200 to 0x23F*/ + uint16_t timer; + uint8_t reserved; + uint8_t ctrl; + } rxsoftimer[16]; + + struct txsoftimer { /*address 0x240 to 0x27F*/ + uint16_t timer; + uint8_t reserved; + uint8_t ctrl; + } txsoftimer[16]; + uint8_t reserved14[132]; + struct rxstaddr { /*address 0x304*/ + uint16_t addr; + uint16_t reserved; + } rxstaddr[15]; + uint8_t reserved15[4]; + struct txstaddr { /*address 0x344*/ + uint16_t addr; + uint16_t reserved; + } txstaddr[15]; + int8_t reserved16[4]; /*address 0x380*/ + struct irqmode { /*address 0x384*/ + int8_t inirqmode; + int8_t reserved21; + int8_t outirqmode; + int8_t reserved22; + } irqmode[15]; + /*The Microprocessor control*/ + uint8_t cpuctrl; /*address 0x3C0*/ + int8_t reserved17[15]; + /*The debug counters and workarounds*/ + uint8_t debug_rx_bcl; /*address 0x3D0*/ + uint8_t debug_rx_bch; /*address 0x3D1*/ + uint8_t debug_rx_status; /*address 0x3D2*/ + uint8_t debug_irq; /*address 0x3D3*/ + uint8_t debug_tx_bcl; /*address 0x3D4*/ + uint8_t debug_tx_bch; /*address 0x3D5*/ + uint8_t debug_tx_status; /*address 0x3D6*/ + uint8_t debug_ien; /*address 0x3D7*/ + uint8_t phywa_en; /*address 0x3D8*/ + /*endian*/ + uint8_t wa1_cnt; /*address 0x3D9*/ + int8_t reserved18[2]; /*address 0x3DA*/ + uint8_t endian_sfr_cs; /*address 0x3DC*/ + int8_t reserved19[2]; /*address 0x3DD*/ + uint8_t endian_sfr_s; /*address 0x3DF*/ + int8_t reserved20[2]; /*address 0x3E0*/ + uint16_t txmaxpack[15]; /*address 0x3E2*/ +}; + +struct CUSTOM_REGS { + uint32_t secure_ctrl; /*address 0x80000*/ + uint32_t secsid_atst; /*address 0x80004*/ + uint32_t nsaid_smmuid; /*address 0x80008*/ + uint32_t ace; /*address 0x8000c*/ + uint32_t wakeup; /*address 0x80010*/ + uint32_t debug; /*address 0x80014*/ +}; + +struct VHUB_REGS { + uint32_t gen_cfg; /*address 0x00*/ + uint32_t gen_st; /*address 0x04*/ + uint32_t bc_cfg; /*address 0x08*/ + uint32_t bc_st; /*address 0x0c*/ + uint32_t adp_cfg; /*address 0x10*/ + uint32_t adp_st; /*address 0x14*/ + uint32_t dbg_cfg; /*address 0x18*/ + uint32_t dbg_st; /*address 0x1c*/ + uint32_t utmip_cfg; /*address 0x20*/ + uint32_t utmip_st; /*address 0x24*/ +}; + +struct DMARegs { + uint32_t conf; /*address 0x400*/ + uint32_t sts; /*address 0x404*/ + uint32_t reserved5[5]; + uint32_t ep_sel; /*address 0x41C*/ + uint32_t traddr; /*address 0x420*/ + uint32_t ep_cfg; /*address 0x424*/ + uint32_t ep_cmd; /*address 0x428*/ + uint32_t ep_sts; /*address 0x42c*/ + uint32_t ep_sts_sid;/*address 0x430*/ + uint32_t ep_sts_en; /*address 0x434*/ + uint32_t drbl; /*address 0x438*/ + uint32_t ep_ien; /*address 0x43C*/ + uint32_t ep_ists; /*address 0x440*/ +}; + +#endif diff --git a/target/linux/phytium/files-5.10/drivers/usb/phytium/pci.c b/target/linux/phytium/files-5.10/drivers/usb/phytium/pci.c new file mode 100644 index 000000000..964fd67bf --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/usb/phytium/pci.c @@ -0,0 +1,208 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include +#include +#include +#include +#include +#include "core.h" + +#define PHYTIUM_OTG_USB_LOADED 3 + +static bool phytium_hw_is_device(struct phytium_cusb *config) +{ + pr_info("%s %d\n", __func__, __LINE__); + + return false; +} + +static bool phytium_hw_is_host(struct phytium_cusb *config) +{ + pr_info("%s %d\n", __func__, __LINE__); + + return true; +} + +static int phytium_get_dr_mode(struct phytium_cusb *config) +{ + enum usb_dr_mode mode; + + config->dr_mode = usb_get_dr_mode(config->dev); + if (config->dr_mode == USB_DR_MODE_UNKNOWN) + config->dr_mode = USB_DR_MODE_OTG; + + mode = config->dr_mode; + if (phytium_hw_is_device(config)) { + if (IS_ENABLED(CONFIG_USB_PHYTIUM_HOST)) { + dev_err(config->dev, "Controller does not support host mode.\n"); + return -EINVAL; + } + + mode = USB_DR_MODE_PERIPHERAL; + } else if (phytium_hw_is_host(config)) { + if (IS_ENABLED(CONFIG_USB_PHYTIUM_PERIPHERAL)) { + dev_err(config->dev, "Controller does not support device mode.\n"); + return -EINVAL; + } + mode = USB_DR_MODE_HOST; + } else { + if (IS_ENABLED(CONFIG_USB_PHYTIUM_HOST)) + mode = USB_DR_MODE_HOST; + else if (IS_ENABLED(CONFIG_USB_PHYTIUM_PERIPHERAL)) + mode = USB_DR_MODE_PERIPHERAL; + } + + if (mode != config->dr_mode) { + dev_warn(config->dev, "Configuration mismatch. dr_mode forced to %s\n", + mode == USB_DR_MODE_HOST ? "host" : "device"); + config->dr_mode = mode; + } + + return 0; +} + +static int phytium_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pid) +{ + struct phytium_cusb *config; + int retval = 0; + + if (usb_disabled()) + return -ENODEV; + + retval = pcim_enable_device(pdev); + if (retval < 0) { + dev_err(&pdev->dev, "pcim_enable_device failed\n"); + return -ENODEV; + } + pci_set_master(pdev); + + config = devm_kzalloc(&pdev->dev, sizeof(*config), GFP_KERNEL); + if (!config) + return -ENOMEM; + + spin_lock_init(&config->lock); + config->dev = &pdev->dev; + + config->irq = pdev->irq; + if (config->irq <= 0) { + dev_err(config->dev, "getting usb irq failed\n"); + return config->irq; + } + + config->regs = pci_iomap(pdev, 0, 0); + if (IS_ERR(config->regs)) { + dev_err(config->dev, "map IOMEM resource failed\n"); + return -1; + } + + if (!pdev->dev.dma_mask) + pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; + + if (dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64))) + dev_err(&pdev->dev, "failed to set 64-bit dma\n"); + else if (dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32))) + dev_err(&pdev->dev, "failed to set 32-bit dma\n"); + + pci_enable_msi(pdev); + + phytium_get_dr_mode(config); + + phytium_core_reset(config, false); + + if (config->dr_mode == USB_DR_MODE_HOST || config->dr_mode == USB_DR_MODE_OTG) + phytium_host_init(config); + + if (config->dr_mode == USB_DR_MODE_PERIPHERAL || config->dr_mode == USB_DR_MODE_OTG) + phytium_gadget_init(config); + + dev_set_drvdata(config->dev, config); + + return 0; +} + +static void phytium_pci_remove(struct pci_dev *pdev) +{ + struct phytium_cusb *config = dev_get_drvdata(&pdev->dev); + + phytium_get_dr_mode(config); + if (config->dr_mode == USB_DR_MODE_HOST || config->dr_mode == USB_DR_MODE_OTG) + phytium_host_uninit(config); + + if (config->dr_mode == USB_DR_MODE_PERIPHERAL || config->dr_mode == USB_DR_MODE_OTG) + phytium_gadget_uninit(config); + + if (config->dr_mode == USB_DR_MODE_PERIPHERAL || config->dr_mode == USB_DR_MODE_OTG) + usb_del_gadget_udc(&config->gadget); + + dev_set_drvdata(&pdev->dev, NULL); + pr_info("%s %d\n", __func__, __LINE__); +} + +static void phytium_pci_shutdown(struct pci_dev *pdev) +{ + struct phytium_cusb *config; + + config = dev_get_drvdata(&pdev->dev); + + phytium_get_dr_mode(config); + + if (config->dr_mode == USB_DR_MODE_PERIPHERAL || config->dr_mode == USB_DR_MODE_OTG) + usb_del_gadget_udc(&config->gadget); +} + +#ifdef CONFIG_PM +static int phytium_pci_resume(struct pci_dev *pdev) +{ + unsigned long flags = 0; + struct phytium_cusb *config; + int ret = 0; + + config = dev_get_drvdata(&pdev->dev); + + spin_lock_irqsave(&config->lock, flags); + ret = phytium_host_resume(config); + spin_unlock_irqrestore(&config->lock, flags); + + return ret; +} + +static int phytium_pci_suspend(struct pci_dev *pdev, pm_message_t state) +{ + unsigned long flags = 0; + struct phytium_cusb *config; + int ret; + + config = dev_get_drvdata(&pdev->dev); + + spin_lock_irqsave(&config->lock, flags); + ret = phytium_host_suspend(config); + spin_unlock_irqrestore(&config->lock, flags); + + return 0; +} +#endif + +const struct pci_device_id phytium_pci_id_table[] = { + {0x10ee, 0x8012, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {} +}; + +static struct pci_driver phytium_otg_driver = { + .name = "phytium_usb", + .id_table = phytium_pci_id_table, + .probe = phytium_pci_probe, + .remove = phytium_pci_remove, + .shutdown = phytium_pci_shutdown, +#ifdef CONFIG_PM + .resume = phytium_pci_resume, + .suspend = phytium_pci_suspend, +#endif +}; + +module_pci_driver(phytium_otg_driver); + +MODULE_AUTHOR("Chen Zhenhua "); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Phytium usb pci wrapper"); diff --git a/target/linux/phytium/files-5.10/drivers/usb/phytium/platform.c b/target/linux/phytium/files-5.10/drivers/usb/phytium/platform.c new file mode 100644 index 000000000..7d39e4b60 --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/usb/phytium/platform.c @@ -0,0 +1,216 @@ +// SPDX-License-Identifier: GPL-3.0 + +#include +#include +//#include +#include +#include +#include +#include +#include +#include +#include "core.h" +#include "hw-regs.h" + +#define PHYTIUM_OTG_USB_LOADED 3 +#define USB2_2_BASE_ADDRESS 0x31800000 + +static const struct of_device_id phytium_otg_of_match[] = { + { + .compatible = "phytium,usb2", + }, + {}, +}; + +#ifdef CONFIG_ACPI +static const struct acpi_device_id phytium_otg_acpi_match[] = { + { "PHYT0037", 0 }, + { } +}; +#endif + +static int phytium_get_dr_mode(struct phytium_cusb *config) +{ + config->dr_mode = usb_get_dr_mode(config->dev); + if (config->dr_mode == USB_DR_MODE_UNKNOWN) + config->dr_mode = USB_DR_MODE_PERIPHERAL; + + return 0; +} + +static irqreturn_t platform_usb_irq(int irq, void *dev_id) +{ + unsigned long flags; + uint8_t otgstate; + + struct phytium_cusb *config = (struct phytium_cusb *)dev_id; + struct GADGET_CTRL *gadget_ctrl = config->gadget_priv; + struct HOST_CTRL *host_ctrl = config->host_priv; + + if (gadget_ctrl || host_ctrl) { + if (host_ctrl) + otgstate = phytium_read8(&host_ctrl->regs->otgstate); + else + otgstate = phytium_read8(&gadget_ctrl->regs->otgstate); + + spin_lock_irqsave(&config->lock, flags); + if (otgstate > HOST_OTG_STATE_A_WAIT_VFALL) + config->gadget_obj->gadget_isr(config->gadget_priv); + else + config->host_obj->host_isr(config->host_priv); + spin_unlock_irqrestore(&config->lock, flags); + } + + return IRQ_HANDLED; +} + +static int phytium_driver_probe(struct platform_device *pdev) +{ + struct phytium_cusb *config; + struct resource *res, *phy_res; + int retval = 0; + + config = devm_kzalloc(&pdev->dev, sizeof(*config), GFP_KERNEL); + if (!config) + return -ENOMEM; + + spin_lock_init(&config->lock); + config->dev = &pdev->dev; + config->isVhubHost = false; + + config->irq = platform_get_irq(pdev, 0); + if (config->irq <= 0) { + dev_err(config->dev, "getting usb irq failed\n"); + return config->irq; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + config->regs = devm_ioremap(&pdev->dev, res->start, resource_size(res)); + if (IS_ERR(config->regs)) { + dev_err(config->dev, "map IOMEM resource failed\n"); + return -1; + } + + phy_res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + config->phy_regs = devm_ioremap(&pdev->dev, phy_res->start, resource_size(phy_res)); + if (IS_ERR(config->phy_regs)) { + dev_err(config->dev, "map IOMEM phy resource failed\n"); + return -1; + } + + phytium_get_dr_mode(config); + + phytium_core_reset(config, false); + + if (config->dr_mode == USB_DR_MODE_HOST || + config->dr_mode == USB_DR_MODE_OTG) { + if (res->start == USB2_2_BASE_ADDRESS) + config->isVhubHost = true; + + phytium_host_init(config); + } + + if (config->dr_mode == USB_DR_MODE_PERIPHERAL || + config->dr_mode == USB_DR_MODE_OTG) + phytium_gadget_init(config); + + dev_set_drvdata(&pdev->dev, config); + + if (config->irq > 0) { + retval = devm_request_irq(config->dev, config->irq, platform_usb_irq, + IRQF_SHARED, "phytium_otg", config); + if (retval != 0) { + dev_err(config->dev, "request irq %d err %d\n", config->irq, retval); + config->irq = 0; + } + } + + return retval; +} + +static int phytium_driver_remove(struct platform_device *dev) +{ + struct phytium_cusb *config = platform_get_drvdata(dev); + + if (!config) + return 0; + + phytium_get_dr_mode(config); + + if (config->dr_mode == USB_DR_MODE_HOST || + config->dr_mode == USB_DR_MODE_OTG) + phytium_host_uninit(config); + + if (config->dr_mode == USB_DR_MODE_PERIPHERAL || + config->dr_mode == USB_DR_MODE_OTG) + phytium_gadget_uninit(config); + + dev_set_drvdata(&dev->dev, NULL); + return 0; +} + +static void phytium_driver_shutdown(struct platform_device *dev) +{ + pr_info("%s %d\n", __func__, __LINE__); +} + +#ifdef CONFIG_PM +static int phytium_driver_suspend(struct device *dev) +{ + struct phytium_cusb *config; + int ret = 0; + + config = dev_get_drvdata(dev); + + if (config->dr_mode == USB_DR_MODE_HOST || + config->dr_mode == USB_DR_MODE_OTG) + ret = phytium_host_suspend(config); + + if (config->dr_mode == USB_DR_MODE_PERIPHERAL || + config->dr_mode == USB_DR_MODE_OTG) + ret = phytium_gadget_suspend(config); + + return ret; +} + +static int phytium_driver_resume(struct device *dev) +{ + struct phytium_cusb *config; + int ret = 0; + + config = dev_get_drvdata(dev); + if (config->dr_mode == USB_DR_MODE_HOST || + config->dr_mode == USB_DR_MODE_OTG) + ret = phytium_host_resume(config); + + if (config->dr_mode == USB_DR_MODE_PERIPHERAL || + config->dr_mode == USB_DR_MODE_OTG) + ret = phytium_gadget_resume(config); + + return ret; +} + +static const struct dev_pm_ops phytium_usb_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(phytium_driver_suspend, phytium_driver_resume) +}; +#endif + +static struct platform_driver phytium_otg_driver = { + .driver = { + .name = "phytium-otg", + .of_match_table = of_match_ptr(phytium_otg_of_match), + .acpi_match_table = ACPI_PTR(phytium_otg_acpi_match), +#ifdef CONFIG_PM + .pm = &phytium_usb_pm_ops, +#endif + }, + .probe = phytium_driver_probe, + .remove = phytium_driver_remove, + .shutdown = phytium_driver_shutdown, +}; + +module_platform_driver(phytium_otg_driver); + +MODULE_AUTHOR("Chen Zhenhua "); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Phytium usb platform wrapper"); diff --git a/target/linux/phytium/files-5.10/drivers/w1/masters/phytium_w1.c b/target/linux/phytium/files-5.10/drivers/w1/masters/phytium_w1.c new file mode 100644 index 000000000..77badce5c --- /dev/null +++ b/target/linux/phytium/files-5.10/drivers/w1/masters/phytium_w1.c @@ -0,0 +1,643 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * drivers/w1/masters/phytium_w1m.c + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PHY_W1M_CTL 0x08 + +/* Simplify mode */ +#define PHY_W1M_CMD 0x04 +#define PHY_W1M_PWM0_START_B 0x30 +#define PHY_W1M_PWM0_END_B 0x34 +#define PHY_W1M_PWM1_START_B 0x38 +#define PHY_W1M_PWM1_END_B 0x3c +#define PHY_W1M_SAMPLE_B 0x40 +#define PHY_W1M_INT_EN_B 0x64 +#define PHY_W1M_INT_STATUS_B 0x74 +#define PHY_W1M_DATA_REG 0x70 + +#define PHY_W1M_CMD_ROM_SEARCH 0xF0 +#define PHY_W1M_CMD_WRITE_BIT 0x35 +#define PHY_W1M_CMD_WRITE_BYTE 0x36 +#define PHY_W1M_CMD_RESET_BUS 0x37 +#define PHY_W1M_CMD_READ_BIT 0x3A +#define PHY_W1M_CMD_READ_BYTE 0x3B +#define PHY_W1M_SLAVE_ROM_ID 0x160 + +#define PHY_W1M_INT_EN_TXCOMPLETE BIT(6) +#define PHY_W1M_INT_EN_RXCOMPLETE BIT(7) + +#define PHY_W1M_INT_STATUS_TXCOMPLETE BIT(6) +#define PHY_W1M_INT_STATUS_RXCOMPLETE BIT(7) + +#define W1M_MOD_W1 1 +#define W1M_MOD_PECI 0 + +#define PHY_W1M_FLAG_CLEAR 0 +#define PHY_W1M_FLAG_SET 1 +#define PHY_W1M_TIMEOUT (HZ/5) + +#define PHY_W1M_MAX_USER 4 + +static DECLARE_WAIT_QUEUE_HEAD(w1m_wait_queue); + +struct w1m_data { + struct device *dev; + void __iomem *w1m_base; + /* lock status update */ + struct mutex w1m_mutex; + int w1m_usecount; + u8 w1m_irqstatus; + /* device lock */ + spinlock_t w1m_spinlock; + /* + * Used to control the call to phytium_w1m_get and phytium_w1m_put. + * Non-w1 Protocol: Write the CMD|REG_address first, followed by + * the data wrire or read. + */ + int init_trans; +}; + +/* W1 register I/O routines */ +static inline u8 phytium_w1m_read(struct w1m_data *w1m_data, u32 offset) +{ + return readl(w1m_data->w1m_base + offset); +} + +static inline void phytium_w1m_write(struct w1m_data *w1m_data, u32 offset, u8 val) +{ + writel(val, w1m_data->w1m_base + offset); +} + +static inline u8 phytium_w1m_merge(struct w1m_data *w1m_data, u32 offset, + u8 val, u8 mask) +{ + u8 new_val = (__raw_readl(w1m_data->w1m_base + offset) & ~mask) + | (val & mask); + writel(new_val, w1m_data->w1m_base + offset); + + return new_val; +} + +static void w1m_disable_interrupt(struct w1m_data *w1m_data, u32 offset, + u32 mask) +{ + u32 ie; + + ie = readl(w1m_data->w1m_base + offset); + writel(ie & mask, w1m_data->w1m_base + offset); +} + +/* write out a byte or bit and fill *status with W1M_INT_STATUS */ +static int phytium_write_data(struct w1m_data *w1m_data, u8 val, u8 *status, bool is_bit) +{ + int ret; + unsigned long irqflags; + + *status = 0; + + spin_lock_irqsave(&w1m_data->w1m_spinlock, irqflags); + /* clear interrupt flags via a dummy read */ + phytium_w1m_read(w1m_data, PHY_W1M_INT_STATUS_B); + /* ISR loads it with new INT_STATUS */ + w1m_data->w1m_irqstatus = 0; + spin_unlock_irqrestore(&w1m_data->w1m_spinlock, irqflags); + + phytium_w1m_merge(w1m_data, PHY_W1M_INT_EN_B, + PHY_W1M_INT_EN_TXCOMPLETE, + PHY_W1M_INT_EN_TXCOMPLETE); + + phytium_w1m_write(w1m_data, PHY_W1M_DATA_REG, val); + phytium_w1m_write(w1m_data, PHY_W1M_CMD, is_bit ? PHY_W1M_CMD_WRITE_BIT : PHY_W1M_CMD_WRITE_BYTE); + + /* wait for the TXCOMPLETE bit */ + ret = wait_event_timeout(w1m_wait_queue, + w1m_data->w1m_irqstatus, PHY_W1M_TIMEOUT); + if (ret == 0) { + dev_err(w1m_data->dev, "TX wait elapsed\n"); + ret = -ETIMEDOUT; + goto out; + } + + *status = w1m_data->w1m_irqstatus; + /* check irqstatus */ + if (!(*status & PHY_W1M_INT_STATUS_TXCOMPLETE)) { + dev_err(w1m_data->dev, + "timeout waiting for TXCOMPLETE/RXCOMPLETE, %x", *status); + ret = -ETIMEDOUT; + } + +out: + return ret; +} + +static irqreturn_t w1m_isr(int irq, void *_w1m) +{ + struct w1m_data *w1m_data = _w1m; + unsigned long irqflags; + + spin_lock_irqsave(&w1m_data->w1m_spinlock, irqflags); + w1m_data->w1m_irqstatus = phytium_w1m_read(w1m_data, PHY_W1M_INT_STATUS_B); + spin_unlock_irqrestore(&w1m_data->w1m_spinlock, irqflags); + + phytium_w1m_write(w1m_data, PHY_W1M_INT_STATUS_B, 0x00); + if (w1m_data->w1m_irqstatus & + (PHY_W1M_INT_STATUS_TXCOMPLETE | PHY_W1M_INT_STATUS_RXCOMPLETE)) { + /* wake up sleeping process */ + wake_up(&w1m_wait_queue); + } + + return IRQ_HANDLED; +} + + +static int phytium_read_data(struct w1m_data *w1m_data, u8 *val, bool is_bit) +{ + int ret = 0; + u8 status; + unsigned long irqflags; + + ret = mutex_lock_interruptible(&w1m_data->w1m_mutex); + if (ret < 0) { + ret = -EINTR; + goto rtn; + } + + if (!w1m_data->w1m_usecount) { + ret = -EINVAL; + goto out; + } + + spin_lock_irqsave(&w1m_data->w1m_spinlock, irqflags); + /* clear interrupt flags via a dummy read */ + phytium_w1m_read(w1m_data, PHY_W1M_INT_STATUS_B); + /* ISR loads it with new INT_STATUS */ + w1m_data->w1m_irqstatus = 0; + spin_unlock_irqrestore(&w1m_data->w1m_spinlock, irqflags); + + phytium_w1m_merge(w1m_data, PHY_W1M_INT_EN_B, + PHY_W1M_INT_EN_RXCOMPLETE, + PHY_W1M_INT_EN_RXCOMPLETE); + + phytium_w1m_write(w1m_data, PHY_W1M_CMD, is_bit ? PHY_W1M_CMD_READ_BIT : PHY_W1M_CMD_READ_BYTE); + + wait_event_timeout(w1m_wait_queue, + (w1m_data->w1m_irqstatus & PHY_W1M_INT_STATUS_RXCOMPLETE), + PHY_W1M_TIMEOUT); + + status = w1m_data->w1m_irqstatus; + /* check irqstatus */ + if (!(status & PHY_W1M_INT_STATUS_RXCOMPLETE)) { + dev_err(w1m_data->dev, "timeout waiting for RXCOMPLETE, %x", status); + ret = -ETIMEDOUT; + goto out; + } + + /* the data is ready. Read it in! */ + *val = phytium_w1m_read(w1m_data, PHY_W1M_DATA_REG); +out: + mutex_unlock(&w1m_data->w1m_mutex); +rtn: + return ret; +} + + +static int phytium_w1m_get(struct w1m_data *w1m_data) +{ + int ret = 0; + + ret = mutex_lock_interruptible(&w1m_data->w1m_mutex); + if (ret < 0) { + ret = -EINTR; + goto rtn; + } + + if (w1m_data->w1m_usecount == PHY_W1M_MAX_USER) { + dev_warn(w1m_data->dev, "attempt to exceed the max use count"); + ret = -EINVAL; + goto out; + } else { + w1m_data->w1m_usecount++; + try_module_get(THIS_MODULE); + if (w1m_data->w1m_usecount == 1) + pm_runtime_get_sync(w1m_data->dev); + } + +out: + mutex_unlock(&w1m_data->w1m_mutex); +rtn: + return ret; +} + +/* Disable clocks to the module */ +static int phytium_w1m_put(struct w1m_data *w1m_data) +{ + int ret = 0; + + ret = mutex_lock_interruptible(&w1m_data->w1m_mutex); + if (ret < 0) + return -EINTR; + + if (w1m_data->w1m_usecount == 0) { + dev_warn(w1m_data->dev, + "attempt to decrement use count when it is zero"); + ret = -EINVAL; + } else { + w1m_data->w1m_usecount--; + module_put(THIS_MODULE); + if (w1m_data->w1m_usecount == 0) + pm_runtime_put_sync(w1m_data->dev); + } + mutex_unlock(&w1m_data->w1m_mutex); + + return ret; +} + +/* + * W1 triplet callback function - used for searching ROM addresses. + * Registered only when controller is in 1-wire mode. + */ +static u8 phytium_w1_triplet(void *_w1m, u8 bdir) +{ + u8 id_bit, comp_bit; + int err; + u8 ret = 0x3; /* no slaves responded */ + struct w1m_data *w1m_data = _w1m; + + phytium_w1m_get(_w1m); + + err = mutex_lock_interruptible(&w1m_data->w1m_mutex); + if (err < 0) { + dev_err(w1m_data->dev, "Could not acquire mutex\n"); + goto rtn; + } + + w1m_data->w1m_irqstatus = 0; + /* read id_bit */ + phytium_w1m_merge(w1m_data, PHY_W1M_INT_EN_B, + PHY_W1M_INT_EN_RXCOMPLETE, + PHY_W1M_INT_EN_RXCOMPLETE); + phytium_w1m_write(w1m_data, PHY_W1M_CMD, 0x3a); + + err = wait_event_timeout(w1m_wait_queue, + (w1m_data->w1m_irqstatus + & PHY_W1M_INT_STATUS_RXCOMPLETE), + PHY_W1M_TIMEOUT); + if (err == 0) { + dev_err(w1m_data->dev, "RX wait elapsed\n"); + goto out; + } + id_bit = (phytium_w1m_read(_w1m, PHY_W1M_DATA_REG) & 0x01); + + w1m_data->w1m_irqstatus = 0; + /* read comp_bit */ + phytium_w1m_merge(w1m_data, PHY_W1M_INT_EN_B, + PHY_W1M_INT_EN_RXCOMPLETE, + PHY_W1M_INT_EN_RXCOMPLETE); + phytium_w1m_write(w1m_data, PHY_W1M_CMD, 0x3a); + err = wait_event_timeout(w1m_wait_queue, + (w1m_data->w1m_irqstatus + & PHY_W1M_INT_STATUS_RXCOMPLETE), + PHY_W1M_TIMEOUT); + if (err == 0) { + dev_err(w1m_data->dev, "RX wait elapsed\n"); + goto out; + } + comp_bit = (phytium_w1m_read(_w1m, PHY_W1M_DATA_REG) & 0x01); + + if (id_bit && comp_bit) { + ret = 0x03; /* no slaves responded */ + goto out; + } + if (!id_bit && !comp_bit) { + /* Both bits are valid, take the direction given */ + ret = bdir ? 0x04 : 0; + } else { + /* Only one bit is valid, take that direction */ + bdir = id_bit; + ret = id_bit ? 0x05 : 0x02; + } + + w1m_data->w1m_irqstatus = 0; + /* write bdir bit */ + phytium_w1m_merge(w1m_data, PHY_W1M_INT_EN_B, + PHY_W1M_INT_EN_TXCOMPLETE, + PHY_W1M_INT_EN_TXCOMPLETE); + + phytium_w1m_write(w1m_data, PHY_W1M_DATA_REG, bdir); + phytium_w1m_write(w1m_data, PHY_W1M_CMD, 0x35); + + err = wait_event_timeout(w1m_wait_queue, + (w1m_data->w1m_irqstatus + & PHY_W1M_INT_STATUS_TXCOMPLETE), + PHY_W1M_TIMEOUT); + if (err == 0) { + dev_err(w1m_data->dev, "TX wait elapsed\n"); + goto out; + } + +out: + mutex_unlock(&w1m_data->w1m_mutex); +rtn: + phytium_w1m_put(_w1m); + return ret; +} + +static u8 phytium_w1_touch_bit(void *_w1m, u8 bit) +{ + u8 result = 0; + int err; + struct w1m_data *w1m_data = _w1m; + + phytium_w1m_get(w1m_data); + + w1m_data->init_trans++; + + err = mutex_lock_interruptible(&w1m_data->w1m_mutex); + if (err < 0) { + dev_err(w1m_data->dev, "Could not acquired mutex\n"); + goto rtn; + } + + w1m_data->w1m_irqstatus = 0; + + phytium_w1m_merge(w1m_data, PHY_W1M_INT_EN_B, + PHY_W1M_INT_EN_RXCOMPLETE, + PHY_W1M_INT_EN_RXCOMPLETE); + phytium_w1m_write(w1m_data, PHY_W1M_CMD, 0x3a); + + err = wait_event_timeout(w1m_wait_queue, + (w1m_data->w1m_irqstatus + & PHY_W1M_INT_STATUS_RXCOMPLETE), + PHY_W1M_TIMEOUT); + if (err == 0) { + dev_info(w1m_data->dev, "Rx wait elapsed\n"); + goto out; + } + + result = (phytium_w1m_read(w1m_data, PHY_W1M_DATA_REG) & 0x01); + + w1m_data->w1m_irqstatus = 0; + +out: + mutex_unlock(&w1m_data->w1m_mutex); + +rtn: + phytium_w1m_put(w1m_data); + + return result; +} + +/* reset callback */ +static u8 phytium_w1_reset_bus(void *_w1m) +{ + phytium_w1m_get(_w1m); + phytium_w1m_write(_w1m, PHY_W1M_CMD, 0x37); + mdelay(1); + phytium_w1m_put(_w1m); + return 0; +} + +/* Read a byte of data from the device */ +static u8 phytium_w1_read_data(void *_w1m, bool is_bit) +{ + struct w1m_data *w1m_data = _w1m; + u8 val = 0; + int ret; + + /* First write to initialize the transfer */ + if (w1m_data->init_trans == 0) + phytium_w1m_get(w1m_data); + + w1m_data->init_trans++; + ret = phytium_read_data(w1m_data, &val, is_bit); + if (ret) { + ret = mutex_lock_interruptible(&w1m_data->w1m_mutex); + if (ret < 0) { + dev_err(w1m_data->dev, "Could not acquire mutex\n"); + return -EINTR; + } + w1m_data->init_trans = 0; + mutex_unlock(&w1m_data->w1m_mutex); + phytium_w1m_put(w1m_data); + return -1; + } + + w1m_disable_interrupt(w1m_data, PHY_W1M_INT_EN_B, + ~((u32)PHY_W1M_INT_EN_RXCOMPLETE)); + + /* Write followed by a read, release the module */ + if (w1m_data->init_trans) { + ret = mutex_lock_interruptible(&w1m_data->w1m_mutex); + if (ret < 0) { + dev_err(w1m_data->dev, "Could not acquire mutex\n"); + return -EINTR; + } + w1m_data->init_trans = 0; + mutex_unlock(&w1m_data->w1m_mutex); + phytium_w1m_put(w1m_data); + } + + return val; +} + +static u8 phytium_w1_read_bit(void *_w1m) +{ + return phytium_w1_read_data(_w1m, true); +} + +static u8 phytium_w1_read_byte(void *_w1m) +{ + return phytium_w1_read_data(_w1m, false); +} + +/* Write a byte of data to the device */ +static void phytium_w1_write_data(void *_w1m, u8 byte, bool is_bit) +{ + struct w1m_data *w1m_data = _w1m; + int ret; + u8 status; + + /* First write to initialize the transfer */ + if (w1m_data->init_trans == 0) + phytium_w1m_get(w1m_data); + + ret = mutex_lock_interruptible(&w1m_data->w1m_mutex); + if (ret < 0) { + dev_err(w1m_data->dev, "Could not acquire mutex\n"); + return; + } + mutex_unlock(&w1m_data->w1m_mutex); + + ret = phytium_write_data(w1m_data, byte, &status, is_bit); + if (ret < 0) { + dev_err(w1m_data->dev, "TX failure:Ctrl status %x\n", status); + return; + } + + w1m_data->init_trans++; + w1m_disable_interrupt(w1m_data, PHY_W1M_INT_EN_B, + ~((u32)PHY_W1M_INT_EN_TXCOMPLETE)); + /* Second write, data transferred. Release the module */ + if (w1m_data->init_trans > 1) { + phytium_w1m_put(w1m_data); + ret = mutex_lock_interruptible(&w1m_data->w1m_mutex); + if (ret < 0) { + dev_err(w1m_data->dev, "Could not acquire mutex\n"); + return; + } + w1m_data->init_trans = 0; + mutex_unlock(&w1m_data->w1m_mutex); + } +} + +static void phytium_w1_write_bit(void *_w1m, u8 bit) +{ + phytium_w1_write_data(_w1m, bit, true); +} + +static void phytium_w1_write_byte(void *_w1m, u8 bit) +{ + phytium_w1_write_data(_w1m, bit, false); +} + +static struct w1_bus_master phytium_w1_master = { + .read_byte = phytium_w1_read_byte, + .write_byte = phytium_w1_write_byte, + .read_bit = phytium_w1_read_bit, + .write_bit = phytium_w1_write_bit, + .reset_bus = phytium_w1_reset_bus, + .touch_bit = phytium_w1_touch_bit, +}; + +static int phytium_w1m_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct w1m_data *w1m_data; + struct resource *res; + int ret, irq; + + w1m_data = devm_kzalloc(dev, sizeof(*w1m_data), GFP_KERNEL); + if (!w1m_data) + return -ENOMEM; + + w1m_data->dev = dev; + platform_set_drvdata(pdev, w1m_data); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + w1m_data->w1m_base = devm_ioremap_resource(dev, res); + if (IS_ERR(w1m_data->w1m_base)) + return PTR_ERR(w1m_data->w1m_base); + + w1m_data->w1m_usecount = 0; + mutex_init(&w1m_data->w1m_mutex); + + pm_runtime_enable(&pdev->dev); + ret = pm_runtime_get_sync(&pdev->dev); + if (ret < 0) { + dev_err(&pdev->dev, "pm_runtime_get_sync failed\n"); + goto err_w1; + } + + spin_lock_init(&w1m_data->w1m_spinlock); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "Failed to get IRQ: %d\n", irq); + ret = irq; + goto err_irq; + } + + ret = devm_request_irq(dev, irq, w1m_isr, 0, "phytium-w1", w1m_data); + if (ret < 0) { + dev_err(&pdev->dev, "could not request irq\n"); + goto err_irq; + } + + pm_runtime_put_sync(&pdev->dev); + + phytium_w1_master.triplet = phytium_w1_triplet; + phytium_w1m_write(w1m_data, PHY_W1M_CTL, 0x10); + phytium_w1m_write(w1m_data, PHY_W1M_INT_EN_B, 0x00); + + phytium_w1_master.data = w1m_data; + + ret = w1_add_master_device(&phytium_w1_master); + if (ret) { + dev_err(&pdev->dev, "Failure in registering w1 master\n"); + goto err_w1; + } + + return 0; + +err_irq: + pm_runtime_put_sync(&pdev->dev); +err_w1: + pm_runtime_disable(&pdev->dev); + + return ret; +} + +static int phytium_w1m_remove(struct platform_device *pdev) +{ + struct w1m_data *w1m_data = platform_get_drvdata(pdev); + + mutex_lock(&w1m_data->w1m_mutex); + + if (w1m_data->w1m_usecount) { + dev_warn(&pdev->dev, "removed when use count is not zero\n"); + mutex_unlock(&w1m_data->w1m_mutex); + return -EBUSY; + } + + mutex_unlock(&w1m_data->w1m_mutex); + + /* remove module dependency */ + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static const struct of_device_id phytium_w1m_dt_ids[] = { + { .compatible = "phytium,w1" }, + { } +}; +MODULE_DEVICE_TABLE(of, phytium_w1m_dt_ids); + +#ifdef CONFIG_ACPI +static const struct acpi_device_id phytium_w1m_acpi_ids[] = { + { "PHYT0034", 0 }, + { } +}; +#endif + +static struct platform_driver phytium_w1m_driver = { + .probe = phytium_w1m_probe, + .remove = phytium_w1m_remove, + .driver = { + .name = "phytium-w1", + .of_match_table = phytium_w1m_dt_ids, + .acpi_match_table = ACPI_PTR(phytium_w1m_acpi_ids), + }, +}; +module_platform_driver(phytium_w1m_driver); + +MODULE_AUTHOR("Zhu Mingshuai "); +MODULE_DESCRIPTION("Phytium w1 bus master driver"); +MODULE_LICENSE("GPL"); diff --git a/target/linux/phytium/files-5.10/sound/pci/hda/hda_phytium.c b/target/linux/phytium/files-5.10/sound/pci/hda/hda_phytium.c new file mode 100644 index 000000000..69bfe699d --- /dev/null +++ b/target/linux/phytium/files-5.10/sound/pci/hda/hda_phytium.c @@ -0,0 +1,1125 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Implementation of primary ALSA driver code for Phytium HD Audio. + * + * Copyright (c) 2018-2023 Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "hda_controller.h" +#include "hda_phytium.h" + +#include "hda_intel_trace.h" + +/* position fix mode */ +enum { + POS_FIX_AUTO, + POS_FIX_LPIB, + POS_FIX_POSBUF, + POS_FIX_VIACOMBO, + POS_FIX_COMBO, +}; + +/* Define IN stream 0 FIFO size offset in VIA controller */ +#define VIA_IN_STREAM0_FIFO_SIZE_OFFSET 0x90 + +/* FT have 4 playback and 4 capture */ +#define FT4C_NUM_CAPTURE 4 +#define FT4C_NUM_PLAYBACK 4 + +#define DWORD_BYTE_WIDTH 4 +#define BYTE_BIT_WIDTH 8 + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; +static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; +static char *model[SNDRV_CARDS]; +static int position_fix[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 1}; +static int bdl_pos_adj[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1}; +static int probe_mask[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1}; +static int probe_only[SNDRV_CARDS]; +static int jackpoll_ms[SNDRV_CARDS]; +static int single_cmd = -1; +static int enable_msi = -1; +#ifdef CONFIG_SND_HDA_INPUT_BEEP +static bool beep_mode[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = + CONFIG_SND_HDA_INPUT_BEEP_MODE}; +#endif + +module_param_array(index, int, NULL, 0444); +MODULE_PARM_DESC(index, "Index value for Intel HD audio interface."); +module_param_array(id, charp, NULL, 0444); +MODULE_PARM_DESC(id, "ID string for Intel HD audio interface."); +module_param_array(enable, bool, NULL, 0444); +MODULE_PARM_DESC(enable, "Enable Intel HD audio interface."); +module_param_array(model, charp, NULL, 0444); +MODULE_PARM_DESC(model, "Use the given board model."); +module_param_array(position_fix, int, NULL, 0444); +MODULE_PARM_DESC(position_fix, "DMA pointer read method. (-1 = system default, 0 = auto, 1 = LPIB, 2 = POSBUF, 3 = VIACOMBO, 4 = COMBO)."); +module_param_array(bdl_pos_adj, int, NULL, 0644); +MODULE_PARM_DESC(bdl_pos_adj, "BDL position adjustment offset."); +module_param_array(probe_mask, int, NULL, 0444); +MODULE_PARM_DESC(probe_mask, "Bitmask to probe codecs (default = -1)."); +module_param_array(probe_only, int, NULL, 0444); +MODULE_PARM_DESC(probe_only, "Only probing and no codec initialization."); +module_param_array(jackpoll_ms, int, NULL, 0444); +MODULE_PARM_DESC(jackpoll_ms, "Ms between polling for jack events (default = 0, using unsol events only)"); +module_param(single_cmd, bint, 0444); +MODULE_PARM_DESC(single_cmd, "Use single command to communicate with codecs (for debugging only)."); +module_param(enable_msi, bint, 0444); +MODULE_PARM_DESC(enable_msi, "Enable Message Signaled Interrupt (MSI)"); +#ifdef CONFIG_SND_HDA_INPUT_BEEP +module_param_array(beep_mode, bool, NULL, 0444); +MODULE_PARM_DESC(beep_mode, "Select HDA Beep registration mode (0=off, 1=on) (default=1)."); +#endif + +#define power_save 0 + +static int align_buffer_size = -1; +module_param(align_buffer_size, bint, 0644); +MODULE_PARM_DESC(align_buffer_size, + "Force buffer and period sizes to be multiple of 128 bytes."); + +/* driver types */ +enum { + AZX_DRIVER_ICH, + AZX_DRIVER_PCH, + AZX_DRIVER_SCH, + AZX_DRIVER_HDMI, + AZX_DRIVER_ATI, + AZX_DRIVER_ATIHDMI, + AZX_DRIVER_ATIHDMI_NS, + AZX_DRIVER_VIA, + AZX_DRIVER_SIS, + AZX_DRIVER_ULI, + AZX_DRIVER_NVIDIA, + AZX_DRIVER_TERA, + AZX_DRIVER_CTX, + AZX_DRIVER_CTHDA, + AZX_DRIVER_CMEDIA, + AZX_DRIVER_GENERIC, + AZX_DRIVER_FT, + AZX_NUM_DRIVERS, /* keep this as last entry */ +}; + +/* NOP for other archs */ +static inline void mark_pages_wc(struct azx *chip, struct snd_dma_buffer *buf, + bool on) +{ +} + +static inline void mark_runtime_wc(struct azx *chip, struct azx_dev *azx_dev, + struct snd_pcm_substream *substream, bool on) +{ +} + +static int azx_acquire_irq(struct azx *chip, int do_disconnect); + +/* calculate runtime delay from LPIB */ +static int azx_get_delay_from_lpib(struct azx *chip, struct azx_dev *azx_dev, + unsigned int pos) +{ + struct snd_pcm_substream *substream = azx_dev->core.substream; + int stream = substream->stream; + unsigned int lpib_pos = azx_get_pos_lpib(chip, azx_dev); + int delay; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + delay = pos - lpib_pos; + else + delay = lpib_pos - pos; + if (delay < 0) { + if (delay >= azx_dev->core.delay_negative_threshold) + delay = 0; + else + delay += azx_dev->core.bufsize; + } + + if (delay >= azx_dev->core.period_bytes) { + dev_info(chip->card->dev, + "Unstable LPIB (%d >= %d); disabling LPIB delay counting\n", + delay, azx_dev->core.period_bytes); + delay = 0; + chip->driver_caps &= ~AZX_DCAPS_COUNT_LPIB_DELAY; + chip->get_delay[stream] = NULL; + } + + return bytes_to_frames(substream->runtime, delay); +} + +static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev); + +/* called from IRQ */ +static int azx_position_check(struct azx *chip, struct azx_dev *azx_dev) +{ + struct hda_ft *hda = container_of(chip, struct hda_ft, chip); + int ok; + + ok = azx_position_ok(chip, azx_dev); + if (ok == 1) { + azx_dev->irq_pending = 0; + return ok; + } else if (ok == 0) { + /* bogus IRQ, process it later */ + azx_dev->irq_pending = 1; + schedule_work(&hda->irq_pending_work); + } + return 0; +} + +static int azx_ft_link_power(struct azx *chip, bool enable) +{ + return 0; +} + +/* + * Check whether the current DMA position is acceptable for updating + * periods. Returns non-zero if it's OK. + * + * Many HD-audio controllers appear pretty inaccurate about + * the update-IRQ timing. The IRQ is issued before actually the + * data is processed. So, we need to process it afterwords in a + * workqueue. + */ +static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev) +{ + struct snd_pcm_substream *substream = azx_dev->core.substream; + int stream = substream->stream; + u32 wallclk; + unsigned int pos; + + wallclk = (azx_readl(chip, WALLCLK) - azx_dev->core.start_wallclk); + + if (wallclk < (azx_dev->core.period_wallclk * 2) / 3) + return -1; /* bogus (too early) interrupt */ + + if (chip->get_position[stream]) + pos = chip->get_position[stream](chip, azx_dev); + else { /* use the position buffer as default */ + pos = azx_get_pos_posbuf(chip, azx_dev); + if (!pos || pos == (u32)-1) { + dev_info(chip->card->dev, + "Invalid position buffer, using LPIB read method instead.\n"); + chip->get_position[stream] = azx_get_pos_lpib; + if (chip->get_position[0] == azx_get_pos_lpib && + chip->get_position[1] == azx_get_pos_lpib) + azx_bus(chip)->use_posbuf = false; + pos = azx_get_pos_lpib(chip, azx_dev); + chip->get_delay[stream] = NULL; + } else { + chip->get_position[stream] = azx_get_pos_posbuf; + if (chip->driver_caps & AZX_DCAPS_COUNT_LPIB_DELAY) + chip->get_delay[stream] = azx_get_delay_from_lpib; + } + } + + if (pos >= azx_dev->core.bufsize) + pos = 0; + + if (WARN_ONCE(!azx_dev->core.period_bytes, + "hda-ft: zero azx_dev->period_bytes")) + return -1; /* this shouldn't happen! */ + if (wallclk < (azx_dev->core.period_wallclk * 5) / 4 && + pos % azx_dev->core.period_bytes > azx_dev->core.period_bytes / 2) + /* NG - it's below the first next period boundary */ + return chip->bdl_pos_adj ? 0 : -1; + + azx_dev->core.start_wallclk += wallclk; + + return 1; /* OK, it's fine */ +} + +static int hda_ft_dma_configure(struct device *dev) +{ + const struct of_device_id *match_of; + const struct acpi_device_id *match_acpi; + + if (dev->of_node) { + match_of = of_match_device(dev->driver->of_match_table, dev); + if (!match_of) { + dev_err(dev, "Error DT match data is missing\n"); + return -ENODEV; + } + set_dma_ops(dev, NULL); + /* + * Because there is no way to transfer to non-coherent dma in + * of_dma_configure if 'dma-coherent' is described in DT, + * use acpi_dma_configure to alloc dma_ops correctly. + */ + acpi_dma_configure(dev, DEV_DMA_NON_COHERENT); + } else if (has_acpi_companion(dev)) { + match_acpi = acpi_match_device(dev->driver->acpi_match_table, dev); + if (!match_acpi) { + dev_err(dev, "Error ACPI match data is missing\n"); + return -ENODEV; + } + set_dma_ops(dev, NULL); + acpi_dma_configure(dev, DEV_DMA_NON_COHERENT); + } + + return 0; +} + +/* The work for pending PCM period updates. */ +static void azx_irq_pending_work(struct work_struct *work) +{ + struct hda_ft *hda = container_of(work, struct hda_ft, irq_pending_work); + struct azx *chip = &hda->chip; + struct hdac_bus *bus = azx_bus(chip); + struct hdac_stream *s; + int pending, ok; + + if (!hda->irq_pending_warned) { + dev_info(chip->card->dev, + "IRQ timing workaround is activated for card #%d. Suggest a bigger bdl_pos_adj.\n", + chip->card->number); + hda->irq_pending_warned = 1; + } + + for (;;) { + pending = 0; + spin_lock_irq(&bus->reg_lock); + list_for_each_entry(s, &bus->stream_list, list) { + struct azx_dev *azx_dev = stream_to_azx_dev(s); + + if (!azx_dev->irq_pending || + !s->substream || !s->running) + continue; + ok = azx_position_ok(chip, azx_dev); + if (ok > 0) { + azx_dev->irq_pending = 0; + spin_unlock(&bus->reg_lock); + snd_pcm_period_elapsed(s->substream); + spin_lock(&bus->reg_lock); + } else if (ok < 0) { + pending = 0; /* too early */ + } else { + pending++; + } + } + spin_unlock_irq(&bus->reg_lock); + if (!pending) + return; + udelay(1000); + } +} + +/* clear irq_pending flags and assure no on-going workq */ +static void azx_clear_irq_pending(struct azx *chip) +{ + struct hdac_bus *bus = azx_bus(chip); + struct hdac_stream *s; + + spin_lock_irq(&bus->reg_lock); + list_for_each_entry(s, &bus->stream_list, list) { + struct azx_dev *azx_dev = stream_to_azx_dev(s); + + azx_dev->irq_pending = 0; + } + spin_unlock_irq(&bus->reg_lock); +} + +static int azx_acquire_irq(struct azx *chip, int do_disconnect) +{ + struct hdac_bus *bus = azx_bus(chip); + + struct hda_ft *hda = container_of(chip, struct hda_ft, chip); + struct platform_device *pdev = to_platform_device(hda->dev); + int irq_id = platform_get_irq(pdev, 0); + int err; + + err = request_irq(irq_id, azx_interrupt, + IRQF_SHARED, KBUILD_MODNAME, chip); + if (err) { + dev_err(chip->card->dev, + "unable to request IRQ %d, disabling device\n", + irq_id); + if (do_disconnect) + snd_card_disconnect(chip->card); + return err; + } + bus->irq = irq_id; + + return 0; +} + +/* get the current DMA position with correction on VIA chips */ +static unsigned int azx_via_get_position(struct azx *chip, + struct azx_dev *azx_dev) +{ + unsigned int link_pos, mini_pos, bound_pos; + unsigned int mod_link_pos, mod_dma_pos, mod_mini_pos; + unsigned int fifo_size; + + link_pos = snd_hdac_stream_get_pos_lpib(azx_stream(azx_dev)); + if (azx_dev->core.substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + /* Playback, no problem using link position */ + return link_pos; + } + + /* Capture */ + /* For new chipset, + * use mod to get the DMA position just like old chipset + */ + mod_dma_pos = le32_to_cpu(*azx_dev->core.posbuf); + mod_dma_pos %= azx_dev->core.period_bytes; + + /* azx_dev->fifo_size can't get FIFO size of in stream. + * Get from base address + offset. + */ + fifo_size = readw(azx_bus(chip)->remap_addr + + VIA_IN_STREAM0_FIFO_SIZE_OFFSET); + + if (azx_dev->insufficient) { + /* Link position never gather than FIFO size */ + if (link_pos <= fifo_size) + return 0; + + azx_dev->insufficient = 0; + } + + if (link_pos <= fifo_size) + mini_pos = azx_dev->core.bufsize + link_pos - fifo_size; + else + mini_pos = link_pos - fifo_size; + + /* Find nearest previous boudary */ + mod_mini_pos = mini_pos % azx_dev->core.period_bytes; + mod_link_pos = link_pos % azx_dev->core.period_bytes; + if (mod_link_pos >= fifo_size) + bound_pos = link_pos - mod_link_pos; + else if (mod_dma_pos >= mod_mini_pos) + bound_pos = mini_pos - mod_mini_pos; + else { + bound_pos = mini_pos - mod_mini_pos + azx_dev->core.period_bytes; + if (bound_pos >= azx_dev->core.bufsize) + bound_pos = 0; + } + + /* Calculate real DMA position we want */ + return bound_pos + mod_dma_pos; +} + +#ifdef CONFIG_PM +static DEFINE_MUTEX(card_list_lock); +static LIST_HEAD(card_list); + +static void azx_add_card_list(struct azx *chip) +{ + struct hda_ft *hda = container_of(chip, struct hda_ft, chip); + + mutex_lock(&card_list_lock); + list_add(&hda->list, &card_list); + mutex_unlock(&card_list_lock); +} + +static void azx_del_card_list(struct azx *chip) +{ + struct hda_ft *hda = container_of(chip, struct hda_ft, chip); + + mutex_lock(&card_list_lock); + list_del_init(&hda->list); + mutex_unlock(&card_list_lock); +} + +#else +#define azx_add_card_list(chip) /* NOP */ +#define azx_del_card_list(chip) /* NOP */ +#endif /* CONFIG_PM */ + +#if defined(CONFIG_PM_SLEEP) +/* power management */ +static int azx_suspend(struct device *dev) +{ + struct snd_card *card = dev_get_drvdata(dev); + struct azx *chip; + struct hda_ft *hda; + struct hdac_bus *bus; + + if (!card) + return 0; + + chip = card->private_data; + hda = container_of(chip, struct hda_ft, chip); + if (chip->disabled || !chip->running) + return 0; + + bus = azx_bus(chip); + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); + azx_clear_irq_pending(chip); + azx_stop_chip(chip); + if (bus->irq >= 0) { + free_irq(bus->irq, (void *)chip); + bus->irq = -1; + } + + return 0; +} + +static int azx_resume(struct device *dev) +{ + struct snd_card *card = dev_get_drvdata(dev); + struct azx *chip; + struct hda_ft *hda; + struct hdac_bus *bus; + int index; + struct snd_pcm_substream *substream; + struct azx_dev *azx_dev; + int err; + + if (!card) + return 0; + + chip = card->private_data; + hda = container_of(chip, struct hda_ft, chip); + bus = azx_bus(chip); + if (chip->disabled || !chip->running) + return 0; + + if (azx_acquire_irq(chip, 1) < 0) + return -EIO; + + index = chip->dev_index; + + snd_hdac_bus_exit_link_reset(bus); + usleep_range(1000, 1200); + + azx_init_chip(chip, 0); + + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + + if (hda->substream && hda->substream->runtime) { + substream = hda->substream; + + if (substream->runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) { + substream->runtime->status->state = + substream->runtime->status->suspended_state; + err = substream->ops->prepare(substream); + if (err < 0) + return err; + } + + azx_dev = get_azx_dev(substream); + hda->substream = NULL; + } + + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +#ifdef CONFIG_PM +static int azx_runtime_suspend(struct device *dev) +{ + struct snd_card *card = dev_get_drvdata(dev); + struct azx *chip; + struct hda_ft *hda; + + if (!card) + return 0; + + chip = card->private_data; + hda = container_of(chip, struct hda_ft, chip); + if (chip->disabled) + return 0; + + if (!azx_has_pm_runtime(chip)) + return 0; + + azx_stop_chip(chip); + azx_enter_link_reset(chip); + azx_clear_irq_pending(chip); + + return 0; +} + +static int azx_runtime_resume(struct device *dev) +{ + struct snd_card *card = dev_get_drvdata(dev); + struct azx *chip; + struct hda_ft *hda; + struct hdac_bus *bus; + struct hda_codec *codec; + int status; + int index; + + if (!card) + return 0; + + chip = card->private_data; + hda = container_of(chip, struct hda_ft, chip); + bus = azx_bus(chip); + if (chip->disabled) + return 0; + + if (!azx_has_pm_runtime(chip)) + return 0; + + /* Read STATESTS before controller reset */ + status = azx_readw(chip, STATESTS); + + index = chip->dev_index; + + snd_hdac_bus_exit_link_reset(bus); + usleep_range(1000, 1200); + + azx_init_chip(chip, 0); + + if (status) { + list_for_each_codec(codec, &chip->bus) + if (status & (1 << codec->addr)) + schedule_delayed_work(&codec->jackpoll_work, + codec->jackpoll_interval); + } + + return 0; +} + +static int azx_runtime_idle(struct device *dev) +{ + struct snd_card *card = dev_get_drvdata(dev); + struct azx *chip; + struct hda_ft *hda; + + if (!card) + return 0; + + chip = card->private_data; + hda = container_of(chip, struct hda_ft, chip); + if (chip->disabled) + return 0; + + if (!azx_has_pm_runtime(chip) || + azx_bus(chip)->codec_powered || !chip->running) + return -EBUSY; + + return 0; +} + +static const struct dev_pm_ops azx_pm = { + SET_SYSTEM_SLEEP_PM_OPS(azx_suspend, azx_resume) + SET_RUNTIME_PM_OPS(azx_runtime_suspend, azx_runtime_resume, azx_runtime_idle) +}; + +#define hda_ft_pm (&azx_pm) +#else +#define hda_ft_pm NULL +#endif /* CONFIG_PM */ + +static int azx_probe_continue(struct azx *chip); + +/* + * destructor + */ +static int azx_free(struct azx *chip) +{ + struct hda_ft *hda = container_of(chip, struct hda_ft, chip); + struct hdac_bus *bus = azx_bus(chip); + struct platform_device *pdev = to_platform_device(hda->dev); + struct device *hddev = hda->dev; + struct resource *res; + resource_size_t size; + + if (azx_has_pm_runtime(chip) && chip->running) + pm_runtime_get_noresume(&pdev->dev); + + azx_del_card_list(chip); + + complete_all(&hda->probe_wait); + + if (bus->chip_init) { + azx_clear_irq_pending(chip); + azx_stop_all_streams(chip); + azx_stop_chip(chip); + } + + if (bus->irq >= 0) { + free_irq(bus->irq, (void*)chip); + bus->irq = -1; + } + + devm_iounmap(hddev, bus->remap_addr); + + azx_free_stream_pages(chip); + azx_free_streams(chip); + snd_hdac_bus_exit(bus); + + if (chip->region_requested){ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + size = resource_size(res); + devm_release_mem_region(hddev, res->start, size); + } + + return 0; +} + +static int azx_dev_disconnect(struct snd_device *device) +{ + struct azx *chip = device->device_data; + + chip->bus.shutdown = 1; + return 0; +} + +static int azx_dev_free(struct snd_device *device) +{ + return azx_free(device->device_data); +} + +static int check_position_fix(struct azx *chip, int fix) +{ + switch (fix) { + case POS_FIX_AUTO: + case POS_FIX_LPIB: + case POS_FIX_POSBUF: + case POS_FIX_VIACOMBO: + case POS_FIX_COMBO: + return fix; + } + + if (chip->driver_caps & AZX_DCAPS_POSFIX_LPIB) { + dev_dbg(chip->card->dev, "Using LPIB position fix\n"); + return POS_FIX_LPIB; + } + return POS_FIX_AUTO; +} + +static void assign_position_fix(struct azx *chip, int fix) +{ + static azx_get_pos_callback_t callbacks[] = { + [POS_FIX_AUTO] = NULL, + [POS_FIX_LPIB] = azx_get_pos_lpib, + [POS_FIX_POSBUF] = azx_get_pos_posbuf, + [POS_FIX_VIACOMBO] = azx_via_get_position, + [POS_FIX_COMBO] = azx_get_pos_lpib, + }; + + chip->get_position[0] = chip->get_position[1] = callbacks[fix]; + + /* combo mode uses LPIB only for playback */ + if (fix == POS_FIX_COMBO) + chip->get_position[1] = NULL; + + if (fix == POS_FIX_POSBUF && + (chip->driver_caps & AZX_DCAPS_COUNT_LPIB_DELAY)) { + chip->get_delay[0] = chip->get_delay[1] = + azx_get_delay_from_lpib; + } + +} + +#define AZX_FORCE_CODEC_MASK 0x100 + +static void check_probe_mask(struct azx *chip, int dev) +{ + chip->codec_probe_mask = probe_mask[dev]; + + /* check forced option */ + if (chip->codec_probe_mask != -1 && + (chip->codec_probe_mask & AZX_FORCE_CODEC_MASK)) { + azx_bus(chip)->codec_mask = chip->codec_probe_mask & 0xff; + dev_info(chip->card->dev, "codec_mask forced to 0x%x\n", + (int)azx_bus(chip)->codec_mask); + } +} + +static void azx_probe_work(struct work_struct *work) +{ + struct hda_ft *hda = container_of(work, struct hda_ft, probe_work); + + azx_probe_continue(&hda->chip); +} + +/* + * constructor + */ +static const struct hda_controller_ops axi_hda_ops; + +static int hda_ft_create(struct snd_card *card, struct platform_device *pdev, + int dev, unsigned int driver_caps, + struct azx **rchip) +{ + static struct snd_device_ops ops = { + .dev_disconnect = azx_dev_disconnect, + .dev_free = azx_dev_free, + }; + struct hda_ft *hda; + struct azx *chip; + int err; + + *rchip = NULL; + + hda = devm_kzalloc(&pdev->dev, sizeof(*hda), GFP_KERNEL); + if (!hda) + return -ENOMEM; + hda->dev = &pdev->dev; + chip = &hda->chip; + mutex_init(&chip->open_mutex); + chip->card = card; + chip->ops = &axi_hda_ops; + chip->driver_caps = driver_caps; + chip->driver_type = driver_caps & 0xff; + chip->dev_index = dev; + if (jackpoll_ms[dev] >= 50 && jackpoll_ms[dev] <= 60000) + chip->jackpoll_interval = msecs_to_jiffies(jackpoll_ms[dev]); + INIT_LIST_HEAD(&chip->pcm_list); + INIT_WORK(&hda->irq_pending_work, azx_irq_pending_work); + INIT_LIST_HEAD(&hda->list); + + init_completion(&hda->probe_wait); + assign_position_fix(chip, check_position_fix(chip, position_fix[dev])); + check_probe_mask(chip, dev); + + if (single_cmd < 0) /* allow fallback to single_cmd at errors */ + chip->fallback_to_single_cmd = 0; + else /* explicitly set to single_cmd or not */ + chip->single_cmd = single_cmd; + + if (bdl_pos_adj[dev] < 0) { + switch (chip->driver_type) { + case AZX_DRIVER_FT: + bdl_pos_adj[dev] = 32; + break; + default: + bdl_pos_adj[dev] = 32; + break; + } + } + chip->bdl_pos_adj = bdl_pos_adj[dev]; + + err = azx_bus_init(chip, model[dev]); + if (err < 0) { + return err; + } + + chip->bus.core.aligned_mmio = 1; + + err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops); + if (err < 0) { + dev_err(card->dev, "Error creating device [card]!\n"); + azx_free(chip); + return err; + } + + /* continue probing in work context as may trigger request module */ + INIT_WORK(&hda->probe_work, azx_probe_work); + + *rchip = chip; + + return 0; +} + +static int azx_first_init(struct azx *chip) +{ + struct hda_ft *hda = container_of(chip, struct hda_ft, chip); + struct platform_device *pdev = to_platform_device(hda->dev); + struct device *hddev = hda->dev; + + int dev = chip->dev_index; + bool full_reset; + + struct snd_card *card = chip->card; + struct hdac_bus *bus = azx_bus(chip); + int err; + unsigned short gcap; + unsigned int dma_bits = 64; + + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + hda->regs = devm_ioremap_resource(hddev, res); + if (IS_ERR(hda->regs)) + return PTR_ERR(hda->regs); + chip->region_requested = 1; + + bus->addr = res->start; + bus->remap_addr = hda->regs; + if (bus->remap_addr == NULL) { + dev_err(card->dev, "ioremap error\n"); + return -ENXIO; + } + + bus->cmd_resend = 1; + + synchronize_irq(bus->irq); + + gcap = azx_readw(chip, GCAP); + dev_dbg(card->dev, "chipset global capabilities = 0x%x\n", gcap); + + /* disable 64bit DMA address on some devices */ + if (chip->driver_caps & AZX_DCAPS_NO_64BIT) { + dev_dbg(card->dev, "Disabling 64bit DMA\n"); + gcap &= ~AZX_GCAP_64OK; + } + + /* disable buffer size rounding to 128-byte multiples if supported */ + if (align_buffer_size >= 0) + chip->align_buffer_size = !!align_buffer_size; + else { + if (chip->driver_caps & AZX_DCAPS_NO_ALIGN_BUFSIZE) + chip->align_buffer_size = 0; + else + chip->align_buffer_size = 1; + } + + err = hda_ft_dma_configure(hddev); + if (err < 0) + return err; + + /* allow 64bit DMA address if supported by H/W */ + if (!(gcap & AZX_GCAP_64OK)) + dma_bits = 32; + if (!dma_set_mask(hddev, DMA_BIT_MASK(dma_bits))) { + dma_set_coherent_mask(hddev, DMA_BIT_MASK(dma_bits)); + } else { + dma_set_mask(hddev, DMA_BIT_MASK(32)); + dma_set_coherent_mask(hddev, DMA_BIT_MASK(32)); + } + + /* read number of streams from GCAP register instead of using + * hardcoded value + */ + chip->capture_streams = (gcap >> 8) & 0x0f; + chip->playback_streams = (gcap >> 12) & 0x0f; + if (!chip->playback_streams && !chip->capture_streams) { + /* gcap didn't give any info, switching to old method */ + chip->playback_streams = FT4C_NUM_PLAYBACK; + chip->capture_streams = FT4C_NUM_CAPTURE; + } + chip->capture_index_offset = 0; + chip->playback_index_offset = chip->capture_streams; + chip->num_streams = chip->playback_streams + chip->capture_streams; + + /* initialize streams */ + err = azx_init_streams(chip); + if (err < 0) + return err; + + err = azx_alloc_stream_pages(chip); + if (err < 0) + return err; + + full_reset = (probe_only[dev] & 2) ? false : true; + azx_init_chip(chip, full_reset); + + /* codec detection */ + if (!azx_bus(chip)->codec_mask) { + dev_err(card->dev, "no codecs found!\n"); + return -ENODEV; + } + + if (azx_acquire_irq(chip, 0) < 0) + return -EBUSY; + + strcpy(card->driver, "ft-hda"); + strcpy(card->shortname, "ft-hda"); + snprintf(card->longname, sizeof(card->longname), + "%s at 0x%lx irq %i", + card->shortname, bus->addr, bus->irq); + + return 0; +} + +static void pcm_mmap_prepare(struct snd_pcm_substream *substream, + struct vm_area_struct *area) +{ + +} + +static const struct hda_controller_ops axi_hda_ops = { + .pcm_mmap_prepare = pcm_mmap_prepare, + .position_check = azx_position_check, + .link_power = azx_ft_link_power, +}; + +static DECLARE_BITMAP(probed_devs, SNDRV_CARDS); + +static int hda_ft_probe(struct platform_device *pdev) +{ + const unsigned int driver_flags = AZX_DRIVER_FT; + struct snd_card *card; + struct hda_ft *hda; + struct azx *chip; + bool schedule_probe; + int err; + int dev; + + dev = find_first_zero_bit(probed_devs, SNDRV_CARDS); + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + set_bit(dev, probed_devs); + return -ENOENT; + } + + err = snd_card_new(&pdev->dev, index[dev], id[dev], THIS_MODULE, + 0, &card); + if (err < 0) { + dev_err(&pdev->dev, "Error creating card!\n"); + return err; + } + + err = hda_ft_create(card, pdev,dev, driver_flags, &chip); + if (err < 0) + goto out_free; + card->private_data = chip; + hda = container_of(chip, struct hda_ft, chip); + + dev_set_drvdata(&pdev->dev, card); + + schedule_probe = !chip->disabled; + + if (schedule_probe) + schedule_work(&hda->probe_work); + + set_bit(dev, probed_devs); + if (chip->disabled) + complete_all(&hda->probe_wait); + return 0; + +out_free: + snd_card_free(card); + return err; +} + +/* number of codec slots for each chipset: 0 = default slots (i.e. 4) */ +static unsigned int azx_max_codecs[AZX_NUM_DRIVERS] = { + [AZX_DRIVER_FT] = 4, +}; + +static int azx_probe_continue(struct azx *chip) +{ + struct hda_ft *hda = container_of(chip, struct hda_ft, chip); + struct device *hddev = hda->dev; + int dev = chip->dev_index; + int err; + struct hdac_bus *bus = azx_bus(chip); + + hda->probe_continued = 1; + + err = azx_first_init(chip); + if (err < 0) + goto out_free; + +#ifdef CONFIG_SND_HDA_INPUT_BEEP + chip->beep_mode = beep_mode[dev]; +#endif + + /* create codec instances */ + err = azx_probe_codecs(chip, azx_max_codecs[chip->driver_type]); + if (err < 0) + goto out_free; + + if ((probe_only[dev] & 1) == 0) { + err = azx_codec_configure(chip); + if (err < 0) + goto out_free; + } + + err = snd_card_register(chip->card); + if (err < 0) + goto out_free; + + chip->running = 1; + azx_add_card_list(chip); + snd_hda_set_power_save(&chip->bus, power_save * 1000); + + if (azx_has_pm_runtime(chip)) + pm_runtime_put_noidle(hddev); + return err; + +out_free: + if (bus->irq >= 0) { + free_irq(bus->irq, (void *)chip); + bus->irq = -1; + } + return err; +} + +static int hda_ft_remove(struct platform_device *pdev) +{ + struct snd_card *card = dev_get_drvdata(&pdev->dev); + struct azx *chip; + struct hda_ft *hda; + + if (card) { + /* cancel the pending probing work */ + chip = card->private_data; + hda = container_of(chip, struct hda_ft, chip); + cancel_work_sync(&hda->probe_work); + clear_bit(chip->dev_index, probed_devs); + + snd_card_free(card); + return 0; + } + return 0; +} + +static void hda_ft_shutdown(struct platform_device *pdev) +{ + struct snd_card *card = dev_get_drvdata(&pdev->dev); + struct azx *chip; + + if (!card) + return; + chip = card->private_data; + if (chip && chip->running) + azx_stop_chip(chip); +} + +static const struct of_device_id hda_ft_of_match[] = { + { .compatible = "phytium,hda" }, + {}, +}; +MODULE_DEVICE_TABLE(of, hda_ft_of_match); + +#ifdef CONFIG_ACPI +static const struct acpi_device_id hda_ft_acpi_match[] = { + { .id = "PHYT0006" }, + {} +}; +MODULE_DEVICE_TABLE(acpi, hda_ft_acpi_match); +#else +#define hda_ft_acpi_match NULL +#endif + +static struct platform_driver ft_platform_hda = { + .driver = { + .name = "ft-hda", + .pm = hda_ft_pm, + .of_match_table = hda_ft_of_match, + .acpi_match_table = hda_ft_acpi_match, + }, + .probe = hda_ft_probe, + .remove = hda_ft_remove, + .shutdown = hda_ft_shutdown, +}; + +module_platform_driver(ft_platform_hda); + +MODULE_DESCRIPTION("FT HDA bus driver"); +MODULE_LICENSE("GPL v2"); diff --git a/target/linux/phytium/files-5.10/sound/pci/hda/hda_phytium.h b/target/linux/phytium/files-5.10/sound/pci/hda/hda_phytium.h new file mode 100644 index 000000000..edca12ec6 --- /dev/null +++ b/target/linux/phytium/files-5.10/sound/pci/hda/hda_phytium.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Implementation of primary ALSA driver code base for Phytium HD Audio. + * + * Copyright (c) 2018-2023 Phytium Technology Co., Ltd. + */ +#ifndef __SOUND_HDA_PHYTIUM_H__ +#define __SOUND_HDA_PHYTIUM_H__ + +#include "hda_controller.h" + +struct hda_ft { + struct azx chip; + struct snd_pcm_substream *substream; + struct device *dev; + void __iomem *regs; + + /* for pending irqs */ + struct work_struct irq_pending_work; + + /* sync probing */ + struct completion probe_wait; + struct work_struct probe_work; + + /* card list (for power_save trigger) */ + struct list_head list; + + /* extra flags */ + unsigned int irq_pending_warned:1; + unsigned int probe_continued:1; + +}; + +#endif diff --git a/target/linux/phytium/files-5.10/sound/soc/codecs/es8336.c b/target/linux/phytium/files-5.10/sound/soc/codecs/es8336.c new file mode 100644 index 000000000..719b7de0e --- /dev/null +++ b/target/linux/phytium/files-5.10/sound/soc/codecs/es8336.c @@ -0,0 +1,1081 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * es8336.c -- es8336 ALSA SoC audio driver + * Copyright Everest Semiconductor Co.,Ltd + * Phytium Information Technology Co.,Ltd + * + * Author: David Yang + * Yiqun Zhang + * + * 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 +#include +#include +#include +#include +#include +#include "es8336.h" + +static struct snd_soc_component *es8336_component; + +static const struct reg_default es8336_reg_defaults[] = { + {0x00, 0x03}, {0x01, 0x03}, {0x02, 0x00}, {0x03, 0x20}, + {0x04, 0x11}, {0x05, 0x00}, {0x06, 0x11}, {0x07, 0x00}, + {0x08, 0x00}, {0x09, 0x01}, {0x0a, 0x00}, {0x0b, 0x00}, + {0x0c, 0xf8}, {0x0d, 0x3f}, {0x0e, 0x00}, {0x0f, 0x00}, + {0x10, 0x01}, {0x11, 0xfc}, {0x12, 0x28}, {0x13, 0x00}, + {0x14, 0x00}, {0x15, 0x33}, {0x16, 0x00}, {0x17, 0x00}, + {0x18, 0x88}, {0x19, 0x06}, {0x1a, 0x22}, {0x1b, 0x03}, + {0x1c, 0x0f}, {0x1d, 0x00}, {0x1e, 0x80}, {0x1f, 0x80}, + {0x20, 0x00}, {0x21, 0x00}, {0x22, 0xc0}, {0x23, 0x00}, + {0x24, 0x01}, {0x25, 0x08}, {0x26, 0x10}, {0x27, 0xc0}, + {0x28, 0x00}, {0x29, 0x1c}, {0x2a, 0x00}, {0x2b, 0xb0}, + {0x2c, 0x32}, {0x2d, 0x03}, {0x2e, 0x00}, {0x2f, 0x11}, + {0x30, 0x10}, {0x31, 0x00}, {0x32, 0x00}, {0x33, 0xc0}, + {0x34, 0xc0}, {0x35, 0x1f}, {0x36, 0xf7}, {0x37, 0xfd}, + {0x38, 0xff}, {0x39, 0x1f}, {0x3a, 0xf7}, {0x3b, 0xfd}, + {0x3c, 0xff}, {0x3d, 0x1f}, {0x3e, 0xf7}, {0x3f, 0xfd}, + {0x40, 0xff}, {0x41, 0x1f}, {0x42, 0xf7}, {0x43, 0xfd}, + {0x44, 0xff}, {0x45, 0x1f}, {0x46, 0xf7}, {0x47, 0xfd}, + {0x48, 0xff}, {0x49, 0x1f}, {0x4a, 0xf7}, {0x4b, 0xfd}, + {0x4c, 0xff}, {0x4d, 0x00}, {0x4e, 0x00}, {0x4f, 0xff}, + {0x50, 0x00}, {0x51, 0x00}, {0x52, 0x00}, {0x53, 0x00}, +}; + +/* codec private data */ +struct es8336_priv { + struct regmap *regmap; + unsigned int dmic_amic; + unsigned int sysclk; + struct snd_pcm_hw_constraint_list *sysclk_constraints; + struct clk *mclk; + int debounce_time; + struct delayed_work work; + + struct gpio_desc *spk_ctl_gpio; + struct gpio_desc *hp_det_gpio; + bool muted; + bool hp_inserted; + + u8 mic_src; + int pwr_count; +}; + +/* + * es8336_reset + * write value 0xff to reg0x00, the chip will be in reset mode + * then, writer 0x00 to reg0x00, unreset the chip + */ +static int es8336_reset(struct snd_soc_component *component) +{ + snd_soc_component_write(component, ES8336_RESET_REG00, 0x3F); + usleep_range(5000, 5500); + return snd_soc_component_write(component, ES8336_RESET_REG00, 0x03); +} + +static void es8336_enable_spk(struct es8336_priv *es8336, bool enable) +{ + gpiod_set_value(es8336->spk_ctl_gpio, enable); +} + +static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -9600, 50, 1); +static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -9600, 50, 1); +static const DECLARE_TLV_DB_SCALE(hpmixer_gain_tlv, -1200, 150, 0); +static const DECLARE_TLV_DB_SCALE(mic_bst_tlv, 0, 1200, 0); + +static unsigned int linin_pga_tlv[] = { + TLV_DB_RANGE_HEAD(9), + 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), + 1, 1, TLV_DB_SCALE_ITEM(300, 0, 0), + 2, 2, TLV_DB_SCALE_ITEM(600, 0, 0), + 3, 3, TLV_DB_SCALE_ITEM(900, 0, 0), + 4, 4, TLV_DB_SCALE_ITEM(1200, 0, 0), + 5, 5, TLV_DB_SCALE_ITEM(1500, 0, 0), + 6, 6, TLV_DB_SCALE_ITEM(1800, 0, 0), + 7, 7, TLV_DB_SCALE_ITEM(2100, 0, 0), + 8, 8, TLV_DB_SCALE_ITEM(2400, 0, 0), +}; + +static unsigned int hpout_vol_tlv[] = { + TLV_DB_RANGE_HEAD(1), + 0, 3, TLV_DB_SCALE_ITEM(-4800, 1200, 0), +}; + +static const char *const alc_func_txt[] = { "Off", "On" }; + +static const struct soc_enum alc_func = + SOC_ENUM_SINGLE(ES8336_ADC_ALC1_REG29, 6, 2, alc_func_txt); + +static const char *const ng_type_txt[] = { + "Constant PGA Gain", "Mute ADC Output" }; + +static const struct soc_enum ng_type = + SOC_ENUM_SINGLE(ES8336_ADC_ALC6_REG2E, 6, 2, ng_type_txt); + +static const char *const adcpol_txt[] = { "Normal", "Invert" }; + +static const struct soc_enum adcpol = + SOC_ENUM_SINGLE(ES8336_ADC_MUTE_REG26, 1, 2, adcpol_txt); + +static const char *const dacpol_txt[] = { + "Normal", "R Invert", "L Invert", "L + R Invert" }; + +static const struct soc_enum dacpol = + SOC_ENUM_SINGLE(ES8336_DAC_SET1_REG30, 0, 4, dacpol_txt); + +static const struct snd_kcontrol_new es8336_snd_controls[] = { + /* HP OUT VOLUME */ + SOC_DOUBLE_TLV("HP Playback Volume", ES8336_CPHP_ICAL_VOL_REG18, + 4, 0, 4, 1, hpout_vol_tlv), + /* HPMIXER VOLUME Control */ + SOC_DOUBLE_TLV("HPMixer Gain", ES8336_HPMIX_VOL_REG16, + 0, 4, 7, 0, hpmixer_gain_tlv), + + /* DAC Digital controls */ + SOC_DOUBLE_R_TLV("DAC Playback Volume", ES8336_DAC_VOLL_REG33, + ES8336_DAC_VOLR_REG34, 0, 0xC0, 1, dac_vol_tlv), + + SOC_SINGLE("Enable DAC Soft Ramp", ES8336_DAC_SET1_REG30, 4, 1, 1), + SOC_SINGLE("DAC Soft Ramp Rate", ES8336_DAC_SET1_REG30, 2, 4, 0), + + SOC_ENUM("Playback Polarity", dacpol), + SOC_SINGLE("DAC Notch Filter", ES8336_DAC_SET2_REG31, 6, 1, 0), + SOC_SINGLE("DAC Double Fs Mode", ES8336_DAC_SET2_REG31, 7, 1, 0), + SOC_SINGLE("DAC Volume Control-LeR", ES8336_DAC_SET2_REG31, 2, 1, 0), + SOC_SINGLE("DAC Stereo Enhancement", ES8336_DAC_SET3_REG32, 0, 7, 0), + + /* +20dB D2SE PGA Control */ + SOC_SINGLE_TLV("MIC Boost", ES8336_ADC_D2SEPGA_REG24, + 0, 1, 0, mic_bst_tlv), + /* 0-+24dB Lineinput PGA Control */ + SOC_SINGLE_TLV("Input PGA", ES8336_ADC_PGAGAIN_REG23, + 4, 8, 0, linin_pga_tlv), +}; + +/* Analog Input MUX */ +static const char * const es8336_analog_in_txt[] = { + "lin1-rin1", + "lin2-rin2", + "lin1-rin1 with 15db Boost", + "lin2-rin2 with 15db Boost" +}; + +static const unsigned int es8336_analog_in_values[] = { 0, 1, 2, 3 }; + +static const struct soc_enum es8336_analog_input_enum = + SOC_VALUE_ENUM_SINGLE(ES8336_ADC_PDN_LINSEL_REG22, 4, 3, + ARRAY_SIZE(es8336_analog_in_txt), + es8336_analog_in_txt, + es8336_analog_in_values); + +static const struct snd_kcontrol_new es8336_analog_in_mux_controls = + SOC_DAPM_ENUM("Route", es8336_analog_input_enum); + +/* Dmic MUX */ +static const char * const es8336_dmic_txt[] = { + "dmic disable", + "dmic data at high level", + "dmic data at low level", +}; + +static const unsigned int es8336_dmic_values[] = { 0, 2, 3 }; + +static const struct soc_enum es8336_dmic_src_enum = + SOC_VALUE_ENUM_SINGLE(ES8336_ADC_DMIC_REG25, 0, 3, + ARRAY_SIZE(es8336_dmic_txt), + es8336_dmic_txt, + es8336_dmic_values); + +static const struct snd_kcontrol_new es8336_dmic_src_controls = + SOC_DAPM_ENUM("Route", es8336_dmic_src_enum); + +/* hp mixer mux */ +static const char *const es8336_hpmux_texts[] = { + "lin1-rin1", + "lin2-rin2", + "lin-rin with Boost", + "lin-rin with Boost and PGA" +}; + +static const unsigned int es8336_hpmux_values[] = { 0, 1, 2, 3 }; + +static const struct soc_enum es8336_left_hpmux_enum = + SOC_VALUE_ENUM_SINGLE(ES8336_HPMIX_SEL_REG13, 4, 7, + ARRAY_SIZE(es8336_hpmux_texts), + es8336_hpmux_texts, + es8336_hpmux_values); + +static const struct snd_kcontrol_new es8336_left_hpmux_controls = + SOC_DAPM_ENUM("Route", es8336_left_hpmux_enum); + +static const struct soc_enum es8336_right_hpmux_enum = + SOC_VALUE_ENUM_SINGLE(ES8336_HPMIX_SEL_REG13, 0, 7, + ARRAY_SIZE(es8336_hpmux_texts), + es8336_hpmux_texts, + es8336_hpmux_values); + +static const struct snd_kcontrol_new es8336_right_hpmux_controls = + SOC_DAPM_ENUM("Route", es8336_right_hpmux_enum); + +/* headphone Output Mixer */ +static const struct snd_kcontrol_new es8336_out_left_mix[] = { + SOC_DAPM_SINGLE("LLIN Switch", ES8336_HPMIX_SWITCH_REG14, + 6, 1, 0), + SOC_DAPM_SINGLE("Left DAC Switch", ES8336_HPMIX_SWITCH_REG14, + 7, 1, 0), +}; + +static const struct snd_kcontrol_new es8336_out_right_mix[] = { + SOC_DAPM_SINGLE("RLIN Switch", ES8336_HPMIX_SWITCH_REG14, + 2, 1, 0), + SOC_DAPM_SINGLE("Right DAC Switch", ES8336_HPMIX_SWITCH_REG14, + 3, 1, 0), +}; + +/* DAC data source mux */ +static const char * const es8336_dacsrc_texts[] = { + "LDATA TO LDAC, RDATA TO RDAC", + "LDATA TO LDAC, LDATA TO RDAC", + "RDATA TO LDAC, RDATA TO RDAC", + "RDATA TO LDAC, LDATA TO RDAC", +}; + +static const unsigned int es8336_dacsrc_values[] = { 0, 1, 2, 3 }; + +static const struct soc_enum es8336_dacsrc_mux_enum = + SOC_VALUE_ENUM_SINGLE(ES8336_DAC_SET1_REG30, 6, 4, + ARRAY_SIZE(es8336_dacsrc_texts), + es8336_dacsrc_texts, + es8336_dacsrc_values); +static const struct snd_kcontrol_new es8336_dacsrc_mux_controls = + SOC_DAPM_ENUM("Route", es8336_dacsrc_mux_enum); + +static const struct snd_soc_dapm_widget es8336_dapm_widgets[] = { + /* Input Lines */ + SND_SOC_DAPM_INPUT("DMIC"), + SND_SOC_DAPM_INPUT("MIC1"), + SND_SOC_DAPM_INPUT("MIC2"), + + SND_SOC_DAPM_MICBIAS("micbias", SND_SOC_NOPM, + 0, 0), + /* Input MUX */ + SND_SOC_DAPM_MUX("Differential Mux", SND_SOC_NOPM, 0, 0, + &es8336_analog_in_mux_controls), + + SND_SOC_DAPM_PGA("Line input PGA", ES8336_ADC_PDN_LINSEL_REG22, + 7, 1, NULL, 0), + + /* ADCs */ + SND_SOC_DAPM_ADC("Mono ADC", NULL, ES8336_ADC_PDN_LINSEL_REG22, 6, 1), + + /* Dmic MUX */ + SND_SOC_DAPM_MUX("Digital Mic Mux", SND_SOC_NOPM, 0, 0, + &es8336_dmic_src_controls), + + /* Digital Interface */ + SND_SOC_DAPM_AIF_OUT("I2S OUT", "I2S1 Capture", 1, + SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_IN("I2S IN", "I2S1 Playback", 0, + SND_SOC_NOPM, 0, 0), + + /* DACs DATA SRC MUX */ + SND_SOC_DAPM_MUX("DAC SRC Mux", SND_SOC_NOPM, 0, 0, + &es8336_dacsrc_mux_controls), + /* DACs */ + SND_SOC_DAPM_DAC("Right DAC", NULL, ES8336_DAC_PDN_REG2F, 0, 1), + SND_SOC_DAPM_DAC("Left DAC", NULL, ES8336_DAC_PDN_REG2F, 4, 1), + + /* Headphone Output Side */ + /* hpmux for hp mixer */ + SND_SOC_DAPM_MUX("Left Hp mux", SND_SOC_NOPM, 0, 0, + &es8336_left_hpmux_controls), + SND_SOC_DAPM_MUX("Right Hp mux", SND_SOC_NOPM, 0, 0, + &es8336_right_hpmux_controls), + /* Output mixer */ + SND_SOC_DAPM_MIXER("Left Hp mixer", ES8336_HPMIX_PDN_REG15, + 4, 1, &es8336_out_left_mix[0], + ARRAY_SIZE(es8336_out_left_mix)), + SND_SOC_DAPM_MIXER("Right Hp mixer", ES8336_HPMIX_PDN_REG15, + 0, 1, &es8336_out_right_mix[0], + ARRAY_SIZE(es8336_out_right_mix)), + SND_SOC_DAPM_MIXER("Left Hp mixer2", SND_SOC_NOPM, + 4, 1, &es8336_out_left_mix[0], + ARRAY_SIZE(es8336_out_left_mix)), + SND_SOC_DAPM_MIXER("Right Hp mixer2", SND_SOC_NOPM, + 0, 1, &es8336_out_right_mix[0], + ARRAY_SIZE(es8336_out_right_mix)), + + /* Output charge pump */ + SND_SOC_DAPM_PGA("HPCP L", ES8336_CPHP_OUTEN_REG17, + 6, 0, NULL, 0), + SND_SOC_DAPM_PGA("HPCP R", ES8336_CPHP_OUTEN_REG17, + 2, 0, NULL, 0), + + /* Output Driver */ + SND_SOC_DAPM_PGA("HPVOL L", ES8336_CPHP_OUTEN_REG17, + 5, 0, NULL, 0), + SND_SOC_DAPM_PGA("HPVOL R", ES8336_CPHP_OUTEN_REG17, + 1, 0, NULL, 0), + /* Output Lines */ + SND_SOC_DAPM_OUTPUT("HPOL"), + SND_SOC_DAPM_OUTPUT("HPOR"), +}; + +static const struct snd_soc_dapm_route es8336_dapm_routes[] = { + /* + * record route map + */ + {"MIC1", NULL, "micbias"}, + {"MIC2", NULL, "micbias"}, + {"DMIC", NULL, "micbias"}, + + {"Differential Mux", "lin1-rin1", "MIC1"}, + {"Differential Mux", "lin2-rin2", "MIC2"}, + {"Differential Mux", "lin1-rin1 with 15db Boost", "MIC1"}, + {"Differential Mux", "lin2-rin2 with 15db Boost", "MIC2"}, + {"Line input PGA", NULL, "Differential Mux"}, + + {"Mono ADC", NULL, "Line input PGA"}, + + {"Digital Mic Mux", "dmic disable", "Mono ADC"}, + {"Digital Mic Mux", "dmic data at high level", "DMIC"}, + {"Digital Mic Mux", "dmic data at low level", "DMIC"}, + + {"I2S OUT", NULL, "Digital Mic Mux"}, + /* + * playback route map + */ + {"DAC SRC Mux", "LDATA TO LDAC, RDATA TO RDAC", "I2S IN"}, + {"DAC SRC Mux", "LDATA TO LDAC, LDATA TO RDAC", "I2S IN"}, + {"DAC SRC Mux", "RDATA TO LDAC, RDATA TO RDAC", "I2S IN"}, + {"DAC SRC Mux", "RDATA TO LDAC, LDATA TO RDAC", "I2S IN"}, + + {"Left DAC", NULL, "DAC SRC Mux"}, + {"Right DAC", NULL, "DAC SRC Mux"}, + + {"Left Hp mux", "lin1-rin1", "MIC1"}, + {"Left Hp mux", "lin2-rin2", "MIC2"}, + {"Left Hp mux", "lin-rin with Boost", "Differential Mux"}, + {"Left Hp mux", "lin-rin with Boost and PGA", "Line input PGA"}, + + {"Right Hp mux", "lin1-rin1", "MIC1"}, + {"Right Hp mux", "lin2-rin2", "MIC2"}, + {"Right Hp mux", "lin-rin with Boost", "Differential Mux"}, + {"Right Hp mux", "lin-rin with Boost and PGA", "Line input PGA"}, + + {"Left Hp mixer", "LLIN Switch", "Left Hp mux"}, + {"Left Hp mixer", "Left DAC Switch", "Left DAC"}, + + {"Right Hp mixer", "RLIN Switch", "Right Hp mux"}, + {"Right Hp mixer", "Right DAC Switch", "Right DAC"}, + + {"HPCP L", NULL, "Left Hp mixer"}, + {"HPCP R", NULL, "Right Hp mixer"}, + + {"HPVOL L", NULL, "HPCP L"}, + {"HPVOL R", NULL, "HPCP R"}, + + {"HPOL", NULL, "HPVOL L"}, + {"HPOR", NULL, "HPVOL R"}, +}; + + +/* The set of rates we can generate from the above for each SYSCLK */ + +static unsigned int rates_12288[] = { + 8000, 12000, 16000, 24000, 24000, 32000, 48000, 96000, +}; + +static struct snd_pcm_hw_constraint_list constraints_12288 = { + .count = ARRAY_SIZE(rates_12288), + .list = rates_12288, +}; + +static unsigned int rates_112896[] = { + 8000, 11025, 22050, 44100, +}; + +static struct snd_pcm_hw_constraint_list constraints_112896 = { + .count = ARRAY_SIZE(rates_112896), + .list = rates_112896, +}; + +static unsigned int rates_12[] = { + 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, + 48000, 88235, 96000, +}; + +static struct snd_pcm_hw_constraint_list constraints_12 = { + .count = ARRAY_SIZE(rates_12), + .list = rates_12, +}; + +/* + * Note that this should be called from init rather than from hw_params. + */ +static int es8336_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct es8336_priv *es8336 = snd_soc_component_get_drvdata(component); + + switch (freq) { + case 11289600: + case 18432000: + case 22579200: + case 36864000: + es8336->sysclk_constraints = &constraints_112896; + es8336->sysclk = freq; + return 0; + case 12288000: + case 19200000: + case 16934400: + case 24576000: + case 33868800: + es8336->sysclk_constraints = &constraints_12288; + es8336->sysclk = freq; + return 0; + case 12000000: + case 24000000: + es8336->sysclk_constraints = &constraints_12; + es8336->sysclk = freq; + return 0; + } + + return 0; +} + +static int es8336_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + u8 iface = 0; + u8 adciface = 0; + u8 daciface = 0; + + iface = snd_soc_component_read(component, ES8336_IFACE); + adciface = snd_soc_component_read(component, ES8336_ADC_IFACE); + daciface = snd_soc_component_read(component, ES8336_DAC_IFACE); + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + iface |= 0x80; + break; + case SND_SOC_DAIFMT_CBS_CFS: + iface &= 0x7F; + break; + default: + return -EINVAL; + } + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + adciface &= 0xFC; + daciface &= 0xFC; + break; + case SND_SOC_DAIFMT_RIGHT_J: + return -EINVAL; + case SND_SOC_DAIFMT_LEFT_J: + adciface &= 0xFC; + daciface &= 0xFC; + adciface |= 0x01; + daciface |= 0x01; + break; + case SND_SOC_DAIFMT_DSP_A: + adciface &= 0xDC; + daciface &= 0xDC; + adciface |= 0x03; + daciface |= 0x03; + break; + case SND_SOC_DAIFMT_DSP_B: + adciface &= 0xDC; + daciface &= 0xDC; + adciface |= 0x23; + daciface |= 0x23; + break; + default: + return -EINVAL; + } + + /* clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + iface &= 0xDF; + adciface &= 0xDF; + daciface &= 0xDF; + break; + case SND_SOC_DAIFMT_IB_IF: + iface |= 0x20; + adciface |= 0x20; + daciface |= 0x20; + break; + case SND_SOC_DAIFMT_IB_NF: + iface |= 0x20; + adciface &= 0xDF; + daciface &= 0xDF; + break; + case SND_SOC_DAIFMT_NB_IF: + iface &= 0xDF; + adciface |= 0x20; + daciface |= 0x20; + break; + default: + return -EINVAL; + } + snd_soc_component_write(component, ES8336_IFACE, iface); + snd_soc_component_write(component, ES8336_ADC_IFACE, adciface); + snd_soc_component_write(component, ES8336_DAC_IFACE, daciface); + return 0; +} + +static int es8336_pcm_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct es8336_priv *es8336 = snd_soc_component_get_drvdata(component); + bool playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); + + snd_soc_component_write(component, ES8336_RESET_REG00, 0xC0); + snd_soc_component_write(component, ES8336_SYS_PDN_REG0D, 0x00); + /* es8336: both playback and capture need dac mclk */ + snd_soc_component_update_bits(component, ES8336_CLKMGR_CLKSW_REG01, + ES8336_CLKMGR_MCLK_DIV_MASK | + ES8336_CLKMGR_DAC_MCLK_MASK, + ES8336_CLKMGR_MCLK_DIV_NML | + ES8336_CLKMGR_DAC_MCLK_EN); + es8336->pwr_count++; + + if (playback) { + snd_soc_component_write(component, ES8336_SYS_LP1_REG0E, 0x3F); + snd_soc_component_write(component, ES8336_SYS_LP2_REG0F, 0x1F); + snd_soc_component_write(component, ES8336_HPMIX_SWITCH_REG14, 0x88); + snd_soc_component_write(component, ES8336_HPMIX_PDN_REG15, 0x00); + snd_soc_component_write(component, ES8336_HPMIX_VOL_REG16, 0xBB); + snd_soc_component_write(component, ES8336_CPHP_PDN2_REG1A, 0x10); + snd_soc_component_write(component, ES8336_CPHP_LDOCTL_REG1B, 0x30); + snd_soc_component_write(component, ES8336_CPHP_PDN1_REG19, 0x02); + snd_soc_component_write(component, ES8336_DAC_PDN_REG2F, 0x00); + snd_soc_component_write(component, ES8336_CPHP_OUTEN_REG17, 0x66); + snd_soc_component_update_bits(component, ES8336_CLKMGR_CLKSW_REG01, + ES8336_CLKMGR_DAC_MCLK_MASK | + ES8336_CLKMGR_DAC_ANALOG_MASK, + ES8336_CLKMGR_DAC_MCLK_EN | + ES8336_CLKMGR_DAC_ANALOG_EN); + msleep(50); + } else { + snd_soc_component_update_bits(component, ES8336_ADC_PDN_LINSEL_REG22, 0xC0, 0x00); + snd_soc_component_update_bits(component, ES8336_CLKMGR_CLKSW_REG01, + ES8336_CLKMGR_ADC_MCLK_MASK | + ES8336_CLKMGR_ADC_ANALOG_MASK, + ES8336_CLKMGR_ADC_MCLK_EN | + ES8336_CLKMGR_ADC_ANALOG_EN); + } + + return 0; +} + +static void es8336_pcm_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct es8336_priv *es8336 = snd_soc_component_get_drvdata(component); + bool playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); + + if (playback) { + snd_soc_component_write(component, ES8336_CPHP_OUTEN_REG17, 0x00); + snd_soc_component_write(component, ES8336_DAC_PDN_REG2F, 0x11); + snd_soc_component_write(component, ES8336_CPHP_LDOCTL_REG1B, 0x03); + snd_soc_component_write(component, ES8336_CPHP_PDN2_REG1A, 0x22); + snd_soc_component_write(component, ES8336_CPHP_PDN1_REG19, 0x06); + snd_soc_component_write(component, ES8336_HPMIX_SWITCH_REG14, 0x00); + snd_soc_component_write(component, ES8336_HPMIX_PDN_REG15, 0x33); + snd_soc_component_write(component, ES8336_HPMIX_VOL_REG16, 0x00); + snd_soc_component_write(component, ES8336_SYS_PDN_REG0D, 0x00); + snd_soc_component_write(component, ES8336_SYS_LP1_REG0E, 0xFF); + snd_soc_component_write(component, ES8336_SYS_LP2_REG0F, 0xFF); + snd_soc_component_update_bits(component, ES8336_CLKMGR_CLKSW_REG01, + ES8336_CLKMGR_DAC_ANALOG_MASK, + ES8336_CLKMGR_DAC_ANALOG_DIS); + } else { + snd_soc_component_update_bits(component, ES8336_ADC_PDN_LINSEL_REG22, 0xC0, 0xc0); + snd_soc_component_update_bits(component, ES8336_CLKMGR_CLKSW_REG01, + ES8336_CLKMGR_ADC_MCLK_MASK | + ES8336_CLKMGR_ADC_ANALOG_MASK, + ES8336_CLKMGR_ADC_MCLK_DIS | + ES8336_CLKMGR_ADC_ANALOG_DIS); + } + + if (--es8336->pwr_count == 0) { + if (!es8336->hp_inserted) + snd_soc_component_write(component, ES8336_SYS_PDN_REG0D, 0x3F); + snd_soc_component_write(component, ES8336_CLKMGR_CLKSW_REG01, 0xF3); + } +} + + +static int es8336_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + int val = 0; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + val = ES8336_DACWL_16; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + val = ES8336_DACWL_20; + break; + case SNDRV_PCM_FORMAT_S24_LE: + val = ES8336_DACWL_24; + break; + case SNDRV_PCM_FORMAT_S32_LE: + val = ES8336_DACWL_32; + break; + default: + val = ES8336_DACWL_16; + break; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + snd_soc_component_update_bits(component, ES8336_SDP_DACFMT_REG0B, + ES8336_DACWL_MASK, val); + else + snd_soc_component_update_bits(component, ES8336_SDP_ADCFMT_REG0A, + ES8336_ADCWL_MASK, val); + + return 0; +} + +static int es8336_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + struct es8336_priv *es8336 = snd_soc_component_get_drvdata(component); + + es8336->muted = mute; + + if (mute) { + es8336_enable_spk(es8336, false); + msleep(100); + snd_soc_component_write(component, ES8336_DAC_SET1_REG30, 0x20); + } + + snd_soc_component_write(component, ES8336_DAC_SET1_REG30, 0x00); + msleep(130); + + if (!es8336->hp_inserted) + es8336_enable_spk(es8336, true); + + return 0; +} + +static int es8336_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct es8336_priv *es8336 = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + break; + + case SND_SOC_BIAS_STANDBY: + break; + + case SND_SOC_BIAS_OFF: + snd_soc_component_write(component, ES8336_CPHP_OUTEN_REG17, 0x00); + snd_soc_component_write(component, ES8336_DAC_PDN_REG2F, 0x11); + snd_soc_component_write(component, ES8336_CPHP_LDOCTL_REG1B, 0x03); + snd_soc_component_write(component, ES8336_CPHP_PDN2_REG1A, 0x22); + snd_soc_component_write(component, ES8336_CPHP_PDN1_REG19, 0x06); + snd_soc_component_write(component, ES8336_HPMIX_SWITCH_REG14, 0x00); + snd_soc_component_write(component, ES8336_HPMIX_PDN_REG15, 0x33); + snd_soc_component_write(component, ES8336_HPMIX_VOL_REG16, 0x00); + snd_soc_component_update_bits(component, ES8336_ADC_PDN_LINSEL_REG22, 0xC0, 0xC0); + if (!es8336->hp_inserted) + snd_soc_component_write(component, ES8336_SYS_PDN_REG0D, 0x3F); + snd_soc_component_write(component, ES8336_SYS_LP1_REG0E, 0x3F); + snd_soc_component_write(component, ES8336_SYS_LP2_REG0F, 0x1F); + snd_soc_component_write(component, ES8336_RESET_REG00, 0x00); + break; + } + + return 0; +} + +#define es8336_RATES SNDRV_PCM_RATE_8000_96000 + +#define es8336_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE) + +static const struct snd_soc_dai_ops es8336_ops = { + .startup = es8336_pcm_startup, + .hw_params = es8336_pcm_hw_params, + .set_fmt = es8336_set_dai_fmt, + .set_sysclk = es8336_set_dai_sysclk, + .mute_stream = es8336_mute, + .shutdown = es8336_pcm_shutdown, +}; + +static struct snd_soc_dai_driver es8336_dai = { + .name = "es8336-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = es8336_RATES, + .formats = es8336_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = es8336_RATES, + .formats = es8336_FORMATS, + }, + .ops = &es8336_ops, + .symmetric_rates = 1, +}; + +static int es8336_init_regs(struct snd_soc_component *component) +{ + struct es8336_priv *es8336 = snd_soc_component_get_drvdata(component); + snd_soc_component_write(component, ES8336_RESET_REG00, 0x3f); + usleep_range(5000, 5500); + snd_soc_component_write(component, ES8336_RESET_REG00, 0x00); + snd_soc_component_write(component, ES8336_SYS_VMIDSEL_REG0C, 0xFF); + msleep(30); + snd_soc_component_write(component, ES8336_CLKMGR_CLKSEL_REG02, 0x08); + snd_soc_component_write(component, ES8336_CLKMGR_ADCOSR_REG03, 0x20); + snd_soc_component_write(component, ES8336_CLKMGR_ADCDIV1_REG04, 0x11); + snd_soc_component_write(component, ES8336_CLKMGR_ADCDIV2_REG05, 0x00); + snd_soc_component_write(component, ES8336_CLKMGR_DACDIV1_REG06, 0x11); + snd_soc_component_write(component, ES8336_CLKMGR_DACDIV2_REG07, 0x00); + snd_soc_component_write(component, ES8336_CLKMGR_CPDIV_REG08, 0x00); + snd_soc_component_write(component, ES8336_SDP_MS_BCKDIV_REG09, 0x04); + snd_soc_component_write(component, ES8336_CLKMGR_CLKSW_REG01, 0x7F); + snd_soc_component_write(component, ES8336_CAL_TYPE_REG1C, 0x0F); + snd_soc_component_write(component, ES8336_CAL_HPLIV_REG1E, 0x90); + snd_soc_component_write(component, ES8336_CAL_HPRIV_REG1F, 0x90); + snd_soc_component_write(component, ES8336_ADC_VOLUME_REG27, 0x00); + snd_soc_component_write(component, ES8336_ADC_PDN_LINSEL_REG22, es8336->mic_src); + snd_soc_component_write(component, ES8336_ADC_D2SEPGA_REG24, 0x00); + snd_soc_component_write(component, ES8336_ADC_DMIC_REG25, 0x08); + snd_soc_component_write(component, ES8336_DAC_SET2_REG31, 0x20); + snd_soc_component_write(component, ES8336_DAC_SET3_REG32, 0x00); + snd_soc_component_write(component, ES8336_DAC_VOLL_REG33, 0x00); + snd_soc_component_write(component, ES8336_DAC_VOLR_REG34, 0x00); + snd_soc_component_write(component, ES8336_SDP_ADCFMT_REG0A, 0x00); + snd_soc_component_write(component, ES8336_SDP_DACFMT_REG0B, 0x00); + snd_soc_component_write(component, ES8336_SYS_VMIDLOW_REG10, 0x11); + snd_soc_component_write(component, ES8336_SYS_VSEL_REG11, 0xFC); + snd_soc_component_write(component, ES8336_SYS_REF_REG12, 0x28); + snd_soc_component_write(component, ES8336_SYS_LP1_REG0E, 0x04); + snd_soc_component_write(component, ES8336_SYS_LP2_REG0F, 0x0C); + snd_soc_component_write(component, ES8336_DAC_PDN_REG2F, 0x11); + snd_soc_component_write(component, ES8336_HPMIX_SEL_REG13, 0x00); + snd_soc_component_write(component, ES8336_HPMIX_SWITCH_REG14, 0x88); + snd_soc_component_write(component, ES8336_HPMIX_PDN_REG15, 0x00); + snd_soc_component_write(component, ES8336_HPMIX_VOL_REG16, 0xBB); + snd_soc_component_write(component, ES8336_CPHP_PDN2_REG1A, 0x10); + snd_soc_component_write(component, ES8336_CPHP_LDOCTL_REG1B, 0x30); + snd_soc_component_write(component, ES8336_CPHP_PDN1_REG19, 0x02); + snd_soc_component_write(component, ES8336_CPHP_ICAL_VOL_REG18, 0x00); + snd_soc_component_write(component, ES8336_GPIO_SEL_REG4D, 0x02); + snd_soc_component_write(component, ES8336_GPIO_DEBUNCE_INT_REG4E, 0x02); + snd_soc_component_write(component, ES8336_TESTMODE_REG50, 0xA0); + snd_soc_component_write(component, ES8336_TEST1_REG51, 0x00); + snd_soc_component_write(component, ES8336_TEST2_REG52, 0x00); + snd_soc_component_write(component, ES8336_SYS_PDN_REG0D, 0x00); + snd_soc_component_write(component, ES8336_RESET_REG00, 0xC0); + msleep(50); + snd_soc_component_write(component, ES8336_ADC_PGAGAIN_REG23, 0x60); + snd_soc_component_write(component, ES8336_ADC_D2SEPGA_REG24, 0x01); + /* adc ds mode, HPF enable */ + snd_soc_component_write(component, ES8336_ADC_DMIC_REG25, 0x08); + snd_soc_component_write(component, ES8336_ADC_ALC1_REG29, 0xcd); + snd_soc_component_write(component, ES8336_ADC_ALC2_REG2A, 0x08); + snd_soc_component_write(component, ES8336_ADC_ALC3_REG2B, 0xa0); + snd_soc_component_write(component, ES8336_ADC_ALC4_REG2C, 0x05); + snd_soc_component_write(component, ES8336_ADC_ALC5_REG2D, 0x06); + snd_soc_component_write(component, ES8336_ADC_ALC6_REG2E, 0x61); + return 0; +} + +static int es8336_suspend(struct snd_soc_component *component) +{ + return 0; +} + +static int es8336_resume(struct snd_soc_component *component) +{ + struct es8336_priv *es8336 = snd_soc_component_get_drvdata(component); + int ret; + + es8336_reset(component); /* UPDATED BY DAVID,15-3-5 */ + ret = snd_soc_component_read(component, ES8336_CLKMGR_ADCDIV2_REG05); + if (!ret) { + es8336_init_regs(component); + snd_soc_component_write(component, ES8336_GPIO_SEL_REG4D, 0x02); + /* max debance time, enable interrupt, low active */ + snd_soc_component_write(component, ES8336_GPIO_DEBUNCE_INT_REG4E, 0xf3); + /* es8336_set_bias_level(component, SND_SOC_BIAS_OFF); */ + snd_soc_component_write(component, ES8336_CPHP_OUTEN_REG17, 0x00); + snd_soc_component_write(component, ES8336_DAC_PDN_REG2F, 0x11); + snd_soc_component_write(component, ES8336_CPHP_LDOCTL_REG1B, 0x03); + snd_soc_component_write(component, ES8336_CPHP_PDN2_REG1A, 0x22); + snd_soc_component_write(component, ES8336_CPHP_PDN1_REG19, 0x06); + snd_soc_component_write(component, ES8336_HPMIX_SWITCH_REG14, 0x00); + snd_soc_component_write(component, ES8336_HPMIX_PDN_REG15, 0x33); + snd_soc_component_write(component, ES8336_HPMIX_VOL_REG16, 0x00); + if (!es8336->hp_inserted) + snd_soc_component_write(component, ES8336_SYS_PDN_REG0D, 0x3F); + snd_soc_component_write(component, ES8336_SYS_LP1_REG0E, 0xFF); + snd_soc_component_write(component, ES8336_SYS_LP2_REG0F, 0xFF); + snd_soc_component_write(component, ES8336_CLKMGR_CLKSW_REG01, 0xF3); + snd_soc_component_update_bits(component, ES8336_ADC_PDN_LINSEL_REG22, 0xC0, 0xC0); + } + return 0; +} + +static irqreturn_t es8336_irq_handler(int irq, void *data) +{ + struct es8336_priv *es8336 = data; + + queue_delayed_work(system_power_efficient_wq, &es8336->work, + msecs_to_jiffies(es8336->debounce_time)); + + return IRQ_HANDLED; +} + +static void hp_work(struct work_struct *work) +{ + struct es8336_priv *es8336; + + es8336 = container_of(work, struct es8336_priv, work.work); + + es8336->hp_inserted = gpiod_get_value(es8336->hp_det_gpio); + if (!es8336->muted) { + if (es8336->hp_inserted) + es8336_enable_spk(es8336, false); + else + es8336_enable_spk(es8336, true); + } +} + +static int es8336_probe(struct snd_soc_component *component) +{ + struct es8336_priv *es8336 = snd_soc_component_get_drvdata(component); + int ret = 0; + + es8336_component = component; + ret = snd_soc_component_read(component, ES8336_CLKMGR_ADCDIV2_REG05); + if (!ret) { + es8336_reset(component); /* UPDATED BY DAVID,15-3-5 */ + ret = snd_soc_component_read(component, ES8336_CLKMGR_ADCDIV2_REG05); + if (!ret) { + es8336_init_regs(component); + snd_soc_component_write(component, ES8336_GPIO_SEL_REG4D, 0x02); + /* max debance time, enable interrupt, low active */ + snd_soc_component_write(component, + ES8336_GPIO_DEBUNCE_INT_REG4E, 0xf3); + + /* es8336_set_bias_level(codec, SND_SOC_BIAS_OFF); */ + snd_soc_component_write(component, ES8336_CPHP_OUTEN_REG17, 0x00); + snd_soc_component_write(component, ES8336_DAC_PDN_REG2F, 0x11); + snd_soc_component_write(component, ES8336_CPHP_LDOCTL_REG1B, 0x03); + snd_soc_component_write(component, ES8336_CPHP_PDN2_REG1A, 0x22); + snd_soc_component_write(component, ES8336_CPHP_PDN1_REG19, 0x06); + snd_soc_component_write(component, ES8336_HPMIX_SWITCH_REG14, 0x00); + snd_soc_component_write(component, ES8336_HPMIX_PDN_REG15, 0x33); + snd_soc_component_write(component, ES8336_HPMIX_VOL_REG16, 0x00); + if (!es8336->hp_inserted) + snd_soc_component_write(component, ES8336_SYS_PDN_REG0D, + 0x3F); + snd_soc_component_write(component, ES8336_SYS_LP1_REG0E, 0xFF); + snd_soc_component_write(component, ES8336_SYS_LP2_REG0F, 0xFF); + snd_soc_component_write(component, ES8336_CLKMGR_CLKSW_REG01, 0xF3); + snd_soc_component_update_bits(component, ES8336_ADC_PDN_LINSEL_REG22, 0xC0, 0xC0); + } + } + + return ret; +} + +static void es8336_remove(struct snd_soc_component *component) +{ + es8336_set_bias_level(component, SND_SOC_BIAS_OFF); +} + +const struct regmap_config es8336_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = ES8336_TEST3_REG53, + .cache_type = REGCACHE_RBTREE, + .reg_defaults = es8336_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(es8336_reg_defaults), +}; + +static const struct snd_soc_component_driver soc_component_dev_es8336 = { + .probe = es8336_probe, + .remove = es8336_remove, + .suspend = es8336_suspend, + .resume = es8336_resume, + .set_bias_level = es8336_set_bias_level, + + .controls = es8336_snd_controls, + .num_controls = ARRAY_SIZE(es8336_snd_controls), + .dapm_widgets = es8336_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(es8336_dapm_widgets), + .dapm_routes = es8336_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(es8336_dapm_routes), +}; + +static int es8336_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct es8336_priv *es8336; + int ret = -1; + int hp_irq; + + es8336 = devm_kzalloc(&i2c->dev, sizeof(*es8336), GFP_KERNEL); + if (!es8336) + return -ENOMEM; + + es8336->debounce_time = 200; + es8336->pwr_count = 0; + es8336->hp_inserted = false; + es8336->muted = true; + + es8336->regmap = devm_regmap_init_i2c(i2c, &es8336_regmap_config); + if (IS_ERR(es8336->regmap)) { + ret = PTR_ERR(es8336->regmap); + dev_err(&i2c->dev, "Failed to init regmap: %d\n", ret); + return ret; + } + + i2c_set_clientdata(i2c, es8336); + + es8336->spk_ctl_gpio = devm_gpiod_get_index_optional(&i2c->dev, "sel", 0, + GPIOD_OUT_HIGH); + ret = of_property_read_u8(i2c->dev.of_node, "mic-src", &es8336->mic_src); + if (ret != 0) { + dev_dbg(&i2c->dev, "mic1-src return %d", ret); + es8336->mic_src = 0x20; + } + dev_dbg(&i2c->dev, "mic1-src %x", es8336->mic_src); + + if (IS_ERR_OR_NULL(es8336->spk_ctl_gpio)) + dev_info(&i2c->dev, "Can not get spk_ctl_gpio\n"); + else + es8336_enable_spk(es8336, false); + + es8336->hp_det_gpio = devm_gpiod_get_index_optional(&i2c->dev, "det", 0, + GPIOD_IN); + + if (IS_ERR_OR_NULL(es8336->hp_det_gpio)) { + dev_info(&i2c->dev, "Can not get hp_det_gpio\n"); + } else { + INIT_DELAYED_WORK(&es8336->work, hp_work); + hp_irq = gpiod_to_irq(es8336->hp_det_gpio); + ret = devm_request_threaded_irq(&i2c->dev, hp_irq, NULL, + es8336_irq_handler, + IRQF_TRIGGER_FALLING | + IRQF_TRIGGER_RISING | + IRQF_ONESHOT, + "es8336_interrupt", es8336); + if (ret < 0) { + dev_err(&i2c->dev, "request_irq failed: %d\n", ret); + return ret; + } + + schedule_delayed_work(&es8336->work, + msecs_to_jiffies(es8336->debounce_time)); + } + + ret = snd_soc_register_component(&i2c->dev, + &soc_component_dev_es8336, + &es8336_dai, 1); + + return ret; +} + +static int es8336_i2c_remove(struct i2c_client *client) +{ + kfree(i2c_get_clientdata(client)); + return 0; +} + +static void es8336_i2c_shutdown(struct i2c_client *client) +{ + struct es8336_priv *es8336 = i2c_get_clientdata(client); + + if (es8336_component != NULL) { + es8336_enable_spk(es8336, false); + msleep(20); + es8336_set_bias_level(es8336_component, SND_SOC_BIAS_OFF); + } +} + +static const struct i2c_device_id es8336_i2c_id[] = { + {"es8336", 0}, + {"10ES8336:00", 0}, + {"10ES8336", 0}, + { } +}; +MODULE_DEVICE_TABLE(i2c, es8336_i2c_id); + +static const struct of_device_id es8336_of_match[] = { + { .compatible = "everest,es8336", }, + { } +}; +MODULE_DEVICE_TABLE(of, es8336_of_match); + +static const struct acpi_device_id es8336_acpi_match[] = { + { "ESSX8336", 0}, + { } +}; +MODULE_DEVICE_TABLE(acpi, es8336_acpi_match); + +static struct i2c_driver es8336_i2c_driver = { + .driver = { + .name = "es8336", + .of_match_table = es8336_of_match, + .acpi_match_table = es8336_acpi_match, + }, + .probe = es8336_i2c_probe, + .remove = es8336_i2c_remove, + .shutdown = es8336_i2c_shutdown, + .id_table = es8336_i2c_id, +}; + +module_i2c_driver(es8336_i2c_driver); +MODULE_DESCRIPTION("ASoC es8336 driver"); +MODULE_LICENSE("GPL"); diff --git a/target/linux/phytium/files-5.10/sound/soc/codecs/es8336.h b/target/linux/phytium/files-5.10/sound/soc/codecs/es8336.h new file mode 100644 index 000000000..d2c74c11f --- /dev/null +++ b/target/linux/phytium/files-5.10/sound/soc/codecs/es8336.h @@ -0,0 +1,161 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright Everest Semiconductor Co.,Ltd + * Phytium Information Technology Co.,Ltd + * + * Author: David Yang + * Yiqun Zhang + * + * 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 _ES8336_H +#define _ES8336_H + +/* ES8336 register space */ +/* + * RESET Control + */ +#define ES8336_RESET_REG00 0x00 +/* + * Clock Managerment + */ +#define ES8336_CLKMGR_CLKSW_REG01 0x01 +#define ES8336_CLKMGR_CLKSEL_REG02 0x02 +#define ES8336_CLKMGR_ADCOSR_REG03 0x03 +#define ES8336_CLKMGR_ADCDIV1_REG04 0x04 +#define ES8336_CLKMGR_ADCDIV2_REG05 0x05 +#define ES8336_CLKMGR_DACDIV1_REG06 0x06 +#define ES8336_CLKMGR_DACDIV2_REG07 0x07 +#define ES8336_CLKMGR_CPDIV_REG08 0x08 +/* + * SDP Control + */ +#define ES8336_SDP_MS_BCKDIV_REG09 0x09 +#define ES8336_SDP_ADCFMT_REG0A 0x0a +#define ES8336_SDP_DACFMT_REG0B 0x0b +/* + * System Control + */ +#define ES8336_SYS_VMIDSEL_REG0C 0x0c +#define ES8336_SYS_PDN_REG0D 0x0d +#define ES8336_SYS_LP1_REG0E 0x0e +#define ES8336_SYS_LP2_REG0F 0x0f +#define ES8336_SYS_VMIDLOW_REG10 0x10 +#define ES8336_SYS_VSEL_REG11 0x11 +#define ES8336_SYS_REF_REG12 0x12 +/* + * HP Mixer + */ +#define ES8336_HPMIX_SEL_REG13 0x13 +#define ES8336_HPMIX_SWITCH_REG14 0x14 +#define ES8336_HPMIX_PDN_REG15 0x15 +#define ES8336_HPMIX_VOL_REG16 0x16 +/* + * Charge Pump Headphone driver + */ +#define ES8336_CPHP_OUTEN_REG17 0x17 +#define ES8336_CPHP_ICAL_VOL_REG18 0x18 +#define ES8336_CPHP_PDN1_REG19 0x19 +#define ES8336_CPHP_PDN2_REG1A 0x1a +#define ES8336_CPHP_LDOCTL_REG1B 0x1b +/* + * Calibration + */ +#define ES8336_CAL_TYPE_REG1C 0x1c +#define ES8336_CAL_SET_REG1D 0x1d +#define ES8336_CAL_HPLIV_REG1E 0x1e +#define ES8336_CAL_HPRIV_REG1F 0x1f +#define ES8336_CAL_HPLMV_REG20 0x20 +#define ES8336_CAL_HPRMV_REG21 0x21 +/* + * ADC Control + */ +#define ES8336_ADC_PDN_LINSEL_REG22 0x22 +#define ES8336_ADC_PGAGAIN_REG23 0x23 +#define ES8336_ADC_D2SEPGA_REG24 0x24 +#define ES8336_ADC_DMIC_REG25 0x25 +#define ES8336_ADC_MUTE_REG26 0x26 +#define ES8336_ADC_VOLUME_REG27 0x27 +#define ES8336_ADC_ALC1_REG29 0x29 +#define ES8336_ADC_ALC2_REG2A 0x2a +#define ES8336_ADC_ALC3_REG2B 0x2b +#define ES8336_ADC_ALC4_REG2C 0x2c +#define ES8336_ADC_ALC5_REG2D 0x2d +#define ES8336_ADC_ALC6_REG2E 0x2e +/* + * DAC Control + */ +#define ES8336_DAC_PDN_REG2F 0x2f +#define ES8336_DAC_SET1_REG30 0x30 +#define ES8336_DAC_SET2_REG31 0x31 +#define ES8336_DAC_SET3_REG32 0x32 +#define ES8336_DAC_VOLL_REG33 0x33 +#define ES8336_DAC_VOLR_REG34 0x34 +/* + * GPIO + */ +#define ES8336_GPIO_SEL_REG4D 0x4D +#define ES8336_GPIO_DEBUNCE_INT_REG4E 0x4E +#define ES8336_GPIO_FLAG 0x4F +/* + * TEST MODE + */ +#define ES8336_TESTMODE_REG50 0x50 +#define ES8336_TEST1_REG51 0x51 +#define ES8336_TEST2_REG52 0x52 +#define ES8336_TEST3_REG53 0x53 + +#define ES8336_IFACE ES8336_SDP_MS_BCKDIV_REG09 +#define ES8336_ADC_IFACE ES8336_SDP_ADCFMT_REG0A +#define ES8336_DAC_IFACE ES8336_SDP_DACFMT_REG0B + +#define ES8336_REGNUM 84 + +/* REGISTER 0X01 CLOCK MANAGER */ +#define ES8336_CLKMGR_MCLK_DIV_MASK (0X1<<7) +#define ES8336_CLKMGR_MCLK_DIV_NML (0X0<<7) +#define ES8336_CLKMGR_MCLK_DIV_1 (0X1<<7) +#define ES8336_CLKMGR_ADC_MCLK_MASK (0X1<<3) +#define ES8336_CLKMGR_ADC_MCLK_EN (0X1<<3) +#define ES8336_CLKMGR_ADC_MCLK_DIS (0X0<<3) +#define ES8336_CLKMGR_DAC_MCLK_MASK (0X1<<2) +#define ES8336_CLKMGR_DAC_MCLK_EN (0X1<<2) +#define ES8336_CLKMGR_DAC_MCLK_DIS (0X0<<2) +#define ES8336_CLKMGR_ADC_ANALOG_MASK (0X1<<1) +#define ES8336_CLKMGR_ADC_ANALOG_EN (0X1<<1) +#define ES8336_CLKMGR_ADC_ANALOG_DIS (0X0<<1) +#define ES8336_CLKMGR_DAC_ANALOG_MASK (0X1<<0) +#define ES8336_CLKMGR_DAC_ANALOG_EN (0X1<<0) +#define ES8336_CLKMGR_DAC_ANALOG_DIS (0X0<<0) + +/* REGISTER 0X0A */ +#define ES8336_ADCWL_MASK (0x7 << 2) +#define ES8336_ADCWL_32 (0x4 << 2) +#define ES8336_ADCWL_24 (0x0 << 2) +#define ES8336_ADCWL_20 (0x1 << 2) +#define ES8336_ADCWL_18 (0x2 << 2) +#define ES8336_ADCWL_16 (0x3 << 2) +#define ES8336_ADCFMT_MASK (0x3 << 0) +#define ES8336_ADCFMT_I2S (0x0 << 0) +#define ES8336_ADCWL_LEFT (0x1 << 0) +#define ES8336_ADCWL_RIGHT (0x2 << 0) +#define ES8336_ADCWL_PCM (0x3 << 0) + +/* REGISTER 0X0B */ +#define ES8336_DACWL_MASK (0x7 << 2) +#define ES8336_DACWL_32 (0x4 << 2) +#define ES8336_DACWL_24 (0x0 << 2) +#define ES8336_DACWL_20 (0x1 << 2) +#define ES8336_DACWL_18 (0x2 << 2) +#define ES8336_DACWL_16 (0x3 << 2) +#define ES8336_DACFMT_MASK (0x3 << 0) +#define ES8336_DACFMT_I2S (0x0 << 0) +#define ES8336_DACWL_LEFT (0x1 << 0) +#define ES8336_DACWL_RIGHT (0x2 << 0) +#define ES8336_DACWL_PCM (0x3 << 0) + +#endif diff --git a/target/linux/phytium/files-5.10/sound/soc/codecs/es8388.c b/target/linux/phytium/files-5.10/sound/soc/codecs/es8388.c new file mode 100644 index 000000000..2824b6306 --- /dev/null +++ b/target/linux/phytium/files-5.10/sound/soc/codecs/es8388.c @@ -0,0 +1,820 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * es8388.c -- ES8388 ALSA SoC Audio driver + * + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + * Author: Yiqun Zhang + * + * 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 "es8388.h" +#include +#include + +static const unsigned int rates_12288[] = { + 8000, 12000, 16000, 24000, 32000, 48000, 96000, +}; + +static const int ratios_12288[] = { + 10, 7, 6, 4, 3, 2, 0, +}; + +static const struct snd_pcm_hw_constraint_list constraints_12288 = { + .count = ARRAY_SIZE(rates_12288), + .list = rates_12288, +}; + +static const unsigned int rates_11289[] = { + 8018, 11025, 22050, 44100, 88200, +}; + +static const int ratios_11289[] = { + 9, 7, 4, 2, 0, +}; + +static const struct snd_pcm_hw_constraint_list constraints_11289 = { + .count = ARRAY_SIZE(rates_11289), + .list = rates_11289, +}; + +#define ES8388_RATES (SNDRV_PCM_RATE_192000 | \ + SNDRV_PCM_RATE_96000 | \ + SNDRV_PCM_RATE_88200 | \ + SNDRV_PCM_RATE_8000_48000) +#define ES8388_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S18_3LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +struct es8388_priv { + struct regmap *regmap; + struct clk *clk; + int playback_fs; + bool deemph; + int mclkdiv2; + const struct snd_pcm_hw_constraint_list *sysclk_constraints; + const int *mclk_ratios; + bool master; +}; + +/* + * ES8388 Controls + */ +static const char * const adcpol_txt[] = {"Normal", "L Invert", "R Invert", + "L + R Invert"}; +static SOC_ENUM_SINGLE_DECL(adcpol, + ES8388_ADCCONTROL6, 6, adcpol_txt); + +static const DECLARE_TLV_DB_SCALE(play_tlv, -3000, 100, 0); +static const DECLARE_TLV_DB_SCALE(dac_adc_tlv, -9600, 50, 0); +static const DECLARE_TLV_DB_SCALE(pga_tlv, 0, 300, 0); +static const DECLARE_TLV_DB_SCALE(bypass_tlv, -1500, 300, 0); +static const DECLARE_TLV_DB_SCALE(mic_tlv, 0, 300, 0); + +static const struct { + int rate; + unsigned int val; +} deemph_settings[] = { + { 0, ES8388_DACCONTROL6_DEEMPH_OFF }, + { 32000, ES8388_DACCONTROL6_DEEMPH_32k }, + { 44100, ES8388_DACCONTROL6_DEEMPH_44_1k }, + { 48000, ES8388_DACCONTROL6_DEEMPH_48k }, +}; + +static int es8388_set_deemph(struct snd_soc_component *component) +{ + struct es8388_priv *es8388 = snd_soc_component_get_drvdata(component); + int val, i, best; + + /* + * If we're using deemphasis select the nearest available sample + * rate. + */ + if (es8388->deemph) { + best = 0; + for (i = 1; i < ARRAY_SIZE(deemph_settings); i++) { + if (abs(deemph_settings[i].rate - es8388->playback_fs) < + abs(deemph_settings[best].rate - es8388->playback_fs)) + best = i; + } + + val = deemph_settings[best].val; + } else { + val = ES8388_DACCONTROL6_DEEMPH_OFF; + } + + dev_dbg(component->dev, "Set deemphasis %d\n", val); + + return snd_soc_component_update_bits(component, ES8388_DACCONTROL6, + ES8388_DACCONTROL6_DEEMPH_MASK, val); +} + +static int es8388_get_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct es8388_priv *es8388 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = es8388->deemph; + return 0; +} + +static int es8388_put_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct es8388_priv *es8388 = snd_soc_component_get_drvdata(component); + unsigned int deemph = ucontrol->value.integer.value[0]; + int ret; + + if (deemph > 1) + return -EINVAL; + + ret = es8388_set_deemph(component); + if (ret < 0) + return ret; + + es8388->deemph = deemph; + + return 0; +} + +static const struct snd_kcontrol_new es8388_snd_controls[] = { + SOC_DOUBLE_R_TLV("Capture Digital Volume", + ES8388_ADCCONTROL8, ES8388_ADCCONTROL9, + 0, 0xc0, 1, dac_adc_tlv), + + SOC_SINGLE_BOOL_EXT("DAC Deemphasis Switch", 0, + es8388_get_deemph, es8388_put_deemph), + + SOC_ENUM("Capture Polarity", adcpol), + + SOC_SINGLE_TLV("Left Mixer Left Bypass Volume", + ES8388_DACCONTROL17, 3, 7, 1, bypass_tlv), + SOC_SINGLE_TLV("Left Mixer Right Bypass Volume", + ES8388_DACCONTROL19, 3, 7, 1, bypass_tlv), + SOC_SINGLE_TLV("Right Mixer Left Bypass Volume", + ES8388_DACCONTROL18, 3, 7, 1, bypass_tlv), + SOC_SINGLE_TLV("Right Mixer Right Bypass Volume", + ES8388_DACCONTROL20, 3, 7, 1, bypass_tlv), + + SOC_DOUBLE_R_TLV("PCM Volume", + ES8388_LDACVOL, ES8388_RDACVOL, + 0, ES8388_DACVOL_MAX, 1, dac_adc_tlv), + + SOC_DOUBLE_R_TLV("Output 1 Playback Volume", + ES8388_LOUT1VOL, ES8388_ROUT1VOL, + 0, ES8388_OUT1VOL_MAX, 0, play_tlv), + + SOC_DOUBLE_R_TLV("Output 2 Playback Volume", + ES8388_LOUT2VOL, ES8388_ROUT2VOL, + 0, ES8388_OUT2VOL_MAX, 0, play_tlv), + + SOC_DOUBLE_TLV("Mic PGA Volume", ES8388_ADCCONTROL1, + 4, 0, 8, 0, mic_tlv), +}; + +/* + * DAPM Controls + */ +static const char * const es8388_line_texts[] = { + "Line 1", "Line 2", "PGA", "Differential"}; + +static const struct soc_enum es8388_lline_enum = + SOC_ENUM_SINGLE(ES8388_DACCONTROL16, 3, + ARRAY_SIZE(es8388_line_texts), + es8388_line_texts); +static const struct snd_kcontrol_new es8388_left_line_controls = + SOC_DAPM_ENUM("Route", es8388_lline_enum); + +static const struct soc_enum es8388_rline_enum = + SOC_ENUM_SINGLE(ES8388_DACCONTROL16, 0, + ARRAY_SIZE(es8388_line_texts), + es8388_line_texts); +static const struct snd_kcontrol_new es8388_right_line_controls = + SOC_DAPM_ENUM("Route", es8388_lline_enum); + +/* Left Mixer */ +static const struct snd_kcontrol_new es8388_left_mixer_controls[] = { + SOC_DAPM_SINGLE("Playback Switch", ES8388_DACCONTROL17, 7, 1, 0), + SOC_DAPM_SINGLE("Left Bypass Switch", ES8388_DACCONTROL17, 6, 1, 0), + SOC_DAPM_SINGLE("Right Playback Switch", ES8388_DACCONTROL18, 7, 1, 0), + SOC_DAPM_SINGLE("Right Bypass Switch", ES8388_DACCONTROL18, 6, 1, 0), +}; + +/* Right Mixer */ +static const struct snd_kcontrol_new es8388_right_mixer_controls[] = { + SOC_DAPM_SINGLE("Left Playback Switch", ES8388_DACCONTROL19, 7, 1, 0), + SOC_DAPM_SINGLE("Left Bypass Switch", ES8388_DACCONTROL19, 6, 1, 0), + SOC_DAPM_SINGLE("Playback Switch", ES8388_DACCONTROL20, 7, 1, 0), + SOC_DAPM_SINGLE("Right Bypass Switch", ES8388_DACCONTROL20, 6, 1, 0), +}; + +static const char * const es8388_pga_sel[] = { + "Line 1", "Line 2", "Line 3", "Differential"}; + +/* Left PGA Mux */ +static const struct soc_enum es8388_lpga_enum = + SOC_ENUM_SINGLE(ES8388_ADCCONTROL2, 6, + ARRAY_SIZE(es8388_pga_sel), + es8388_pga_sel); +static const struct snd_kcontrol_new es8388_left_pga_controls = + SOC_DAPM_ENUM("Route", es8388_lpga_enum); + +/* Right PGA Mux */ +static const struct soc_enum es8388_rpga_enum = + SOC_ENUM_SINGLE(ES8388_ADCCONTROL2, 4, + ARRAY_SIZE(es8388_pga_sel), + es8388_pga_sel); +static const struct snd_kcontrol_new es8388_right_pga_controls = + SOC_DAPM_ENUM("Route", es8388_rpga_enum); + +/* Differential Mux */ +static const char * const es8388_diff_sel[] = {"Line 1", "Line 2"}; +static SOC_ENUM_SINGLE_DECL(diffmux, + ES8388_ADCCONTROL3, 7, es8388_diff_sel); +static const struct snd_kcontrol_new es8388_diffmux_controls = + SOC_DAPM_ENUM("Route", diffmux); + +/* Mono ADC Mux */ +static const char * const es8388_mono_mux[] = {"Stereo", "Mono (Left)", + "Mono (Right)", "Digital Mono"}; +static SOC_ENUM_SINGLE_DECL(monomux, + ES8388_ADCCONTROL3, 3, es8388_mono_mux); +static const struct snd_kcontrol_new es8388_monomux_controls = + SOC_DAPM_ENUM("Route", monomux); + +static const struct snd_soc_dapm_widget es8388_dapm_widgets[] = { + SND_SOC_DAPM_MUX("Differential Mux", SND_SOC_NOPM, 0, 0, + &es8388_diffmux_controls), + SND_SOC_DAPM_MUX("Left ADC Mux", SND_SOC_NOPM, 0, 0, + &es8388_monomux_controls), + SND_SOC_DAPM_MUX("Right ADC Mux", SND_SOC_NOPM, 0, 0, + &es8388_monomux_controls), + + SND_SOC_DAPM_MUX("Left PGA Mux", ES8388_ADCPOWER, + ES8388_ADCPOWER_AINL_OFF, 1, + &es8388_left_pga_controls), + SND_SOC_DAPM_MUX("Right PGA Mux", ES8388_ADCPOWER, + ES8388_ADCPOWER_AINR_OFF, 1, + &es8388_right_pga_controls), + + SND_SOC_DAPM_MUX("Left Line Mux", SND_SOC_NOPM, 0, 0, + &es8388_left_line_controls), + SND_SOC_DAPM_MUX("Right Line Mux", SND_SOC_NOPM, 0, 0, + &es8388_right_line_controls), + + SND_SOC_DAPM_ADC("Right ADC", "Right Capture", ES8388_ADCPOWER, + ES8388_ADCPOWER_ADCR_OFF, 1), + SND_SOC_DAPM_ADC("Left ADC", "Left Capture", ES8388_ADCPOWER, + ES8388_ADCPOWER_ADCL_OFF, 1), + + SND_SOC_DAPM_SUPPLY("DAC STM", ES8388_CHIPPOWER, + ES8388_CHIPPOWER_DACSTM_RESET, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC STM", ES8388_CHIPPOWER, + ES8388_CHIPPOWER_ADCSTM_RESET, 1, NULL, 0), + + SND_SOC_DAPM_SUPPLY("DAC DIG", ES8388_CHIPPOWER, + ES8388_CHIPPOWER_DACDIG_OFF, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC DIG", ES8388_CHIPPOWER, + ES8388_CHIPPOWER_ADCDIG_OFF, 1, NULL, 0), + + SND_SOC_DAPM_SUPPLY("DAC DLL", ES8388_CHIPPOWER, + ES8388_CHIPPOWER_DACDLL_OFF, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC DLL", ES8388_CHIPPOWER, + ES8388_CHIPPOWER_ADCDLL_OFF, 1, NULL, 0), + + SND_SOC_DAPM_SUPPLY("ADC Vref", ES8388_CHIPPOWER, + ES8388_CHIPPOWER_ADCVREF_OFF, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC Vref", ES8388_CHIPPOWER, + ES8388_CHIPPOWER_DACVREF_OFF, 1, NULL, 0), + + SND_SOC_DAPM_DAC("Right DAC", "Right Playback", ES8388_DACPOWER, + ES8388_DACPOWER_RDAC_OFF, 1), + SND_SOC_DAPM_DAC("Left DAC", "Left Playback", ES8388_DACPOWER, + ES8388_DACPOWER_LDAC_OFF, 1), + + SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0, + &es8388_left_mixer_controls[0], + ARRAY_SIZE(es8388_left_mixer_controls)), + SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0, + &es8388_right_mixer_controls[0], + ARRAY_SIZE(es8388_right_mixer_controls)), + + SND_SOC_DAPM_PGA("Right Out 2", ES8388_DACPOWER, + ES8388_DACPOWER_ROUT2_ON, 0, NULL, 0), + SND_SOC_DAPM_PGA("Left Out 2", ES8388_DACPOWER, + ES8388_DACPOWER_LOUT2_ON, 0, NULL, 0), + SND_SOC_DAPM_PGA("Right Out 1", ES8388_DACPOWER, + ES8388_DACPOWER_ROUT1_ON, 0, NULL, 0), + SND_SOC_DAPM_PGA("Left Out 1", ES8388_DACPOWER, + ES8388_DACPOWER_LOUT1_ON, 0, NULL, 0), + + SND_SOC_DAPM_OUTPUT("LOUT1"), + SND_SOC_DAPM_OUTPUT("ROUT1"), + SND_SOC_DAPM_OUTPUT("LOUT2"), + SND_SOC_DAPM_OUTPUT("ROUT2"), + + SND_SOC_DAPM_INPUT("LINPUT1"), + SND_SOC_DAPM_INPUT("LINPUT2"), + SND_SOC_DAPM_INPUT("RINPUT1"), + SND_SOC_DAPM_INPUT("RINPUT2"), +}; + +static const struct snd_soc_dapm_route es8388_dapm_routes[] = { + { "Left Line Mux", "Line 1", "LINPUT1" }, + { "Left Line Mux", "Line 2", "LINPUT2" }, + { "Left Line Mux", "PGA", "Left PGA Mux" }, + { "Left Line Mux", "Differential", "Differential Mux" }, + + { "Right Line Mux", "Line 1", "RINPUT1" }, + { "Right Line Mux", "Line 2", "RINPUT2" }, + { "Right Line Mux", "PGA", "Right PGA Mux" }, + { "Right Line Mux", "Differential", "Differential Mux" }, + + { "Left PGA Mux", "Line 1", "LINPUT1" }, + { "Left PGA Mux", "Line 2", "LINPUT2" }, + { "Left PGA Mux", "Differential", "Differential Mux" }, + + { "Right PGA Mux", "Line 1", "RINPUT1" }, + { "Right PGA Mux", "Line 2", "RINPUT2" }, + { "Right PGA Mux", "Differential", "Differential Mux" }, + + { "Differential Mux", "Line 1", "LINPUT1" }, + { "Differential Mux", "Line 1", "RINPUT1" }, + { "Differential Mux", "Line 2", "LINPUT2" }, + { "Differential Mux", "Line 2", "RINPUT2" }, + + { "Left ADC Mux", "Stereo", "Left PGA Mux" }, + { "Left ADC Mux", "Mono (Left)", "Left PGA Mux" }, + { "Left ADC Mux", "Digital Mono", "Left PGA Mux" }, + + { "Right ADC Mux", "Stereo", "Right PGA Mux" }, + { "Right ADC Mux", "Mono (Right)", "Right PGA Mux" }, + { "Right ADC Mux", "Digital Mono", "Right PGA Mux" }, + + { "Left ADC", NULL, "Left ADC Mux" }, + { "Right ADC", NULL, "Right ADC Mux" }, + + { "ADC DIG", NULL, "ADC STM" }, + { "ADC DIG", NULL, "ADC Vref" }, + { "ADC DIG", NULL, "ADC DLL" }, + + { "Left ADC", NULL, "ADC DIG" }, + { "Right ADC", NULL, "ADC DIG" }, + + { "Left Line Mux", "Line 1", "LINPUT1" }, + { "Left Line Mux", "Line 2", "LINPUT2" }, + { "Left Line Mux", "PGA", "Left PGA Mux" }, + { "Left Line Mux", "Differential", "Differential Mux" }, + + { "Right Line Mux", "Line 1", "RINPUT1" }, + { "Right Line Mux", "Line 2", "RINPUT2" }, + { "Right Line Mux", "PGA", "Right PGA Mux" }, + { "Right Line Mux", "Differential", "Differential Mux" }, + + { "Left Out 1", NULL, "Left DAC" }, + { "Right Out 1", NULL, "Right DAC" }, + { "Left Out 2", NULL, "Left DAC" }, + { "Right Out 2", NULL, "Right DAC" }, + + { "Left Mixer", "Playback Switch", "Left DAC" }, + { "Left Mixer", "Left Bypass Switch", "Left Line Mux" }, + { "Left Mixer", "Right Playback Switch", "Right DAC" }, + { "Left Mixer", "Right Bypass Switch", "Right Line Mux" }, + + { "Right Mixer", "Left Playback Switch", "Left DAC" }, + { "Right Mixer", "Left Bypass Switch", "Left Line Mux" }, + { "Right Mixer", "Playback Switch", "Right DAC" }, + { "Right Mixer", "Right Bypass Switch", "Right Line Mux" }, + + { "DAC DIG", NULL, "DAC STM" }, + { "DAC DIG", NULL, "DAC Vref" }, + { "DAC DIG", NULL, "DAC DLL" }, + + { "Left DAC", NULL, "DAC DIG" }, + { "Right DAC", NULL, "DAC DIG" }, + + { "Left Out 1", NULL, "Left Mixer" }, + { "LOUT1", NULL, "Left Out 1" }, + { "Right Out 1", NULL, "Right Mixer" }, + { "ROUT1", NULL, "Right Out 1" }, + + { "Left Out 2", NULL, "Left Mixer" }, + { "LOUT2", NULL, "Left Out 2" }, + { "Right Out 2", NULL, "Right Mixer" }, + { "ROUT2", NULL, "Right Out 2" }, +}; + +static int es8388_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + return snd_soc_component_update_bits(dai->component, ES8388_DACCONTROL3, + ES8388_DACCONTROL3_DACMUTE, + mute ? ES8388_DACCONTROL3_DACMUTE : 0); +} + +static int es8388_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct es8388_priv *es8388 = snd_soc_component_get_drvdata(component); + + if (es8388->master && es8388->sysclk_constraints) + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + es8388->sysclk_constraints); + + return 0; +} + +static int es8388_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct es8388_priv *es8388 = snd_soc_component_get_drvdata(component); + int i; + int reg; + int wl; + int ratio; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + reg = ES8388_DACCONTROL2; + else + reg = ES8388_ADCCONTROL5; + + if (es8388->master) { + if (!es8388->sysclk_constraints) { + dev_err(component->dev, "No MCLK configured\n"); + return -EINVAL; + } + + for (i = 0; i < es8388->sysclk_constraints->count; i++) + if (es8388->sysclk_constraints->list[i] == + params_rate(params)) + break; + + if (i == es8388->sysclk_constraints->count) { + dev_err(component->dev, + "LRCLK %d unsupported with current clock\n", + params_rate(params)); + return -EINVAL; + } + ratio = es8388->mclk_ratios[i]; + } else { + ratio = 0; + es8388->mclkdiv2 = 0; + } + + snd_soc_component_update_bits(component, ES8388_MASTERMODE, + ES8388_MASTERMODE_MCLKDIV2, + es8388->mclkdiv2 ? ES8388_MASTERMODE_MCLKDIV2 : 0); + + switch (params_width(params)) { + case 16: + wl = 3; + break; + case 18: + wl = 2; + break; + case 20: + wl = 1; + break; + case 24: + wl = 0; + break; + case 32: + wl = 4; + break; + default: + return -EINVAL; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + snd_soc_component_update_bits(component, ES8388_DACCONTROL1, + ES8388_DACCONTROL1_DACWL_MASK, + wl << ES8388_DACCONTROL1_DACWL_SHIFT); + + es8388->playback_fs = params_rate(params); + es8388_set_deemph(component); + } else + snd_soc_component_update_bits(component, ES8388_ADCCONTROL4, + ES8388_ADCCONTROL4_ADCWL_MASK, + wl << ES8388_ADCCONTROL4_ADCWL_SHIFT); + + return snd_soc_component_update_bits(component, reg, ES8388_RATEMASK, ratio); +} + +static int es8388_set_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct es8388_priv *es8388 = snd_soc_component_get_drvdata(component); + int mclkdiv2 = 0; + + switch (freq) { + case 0: + es8388->sysclk_constraints = NULL; + es8388->mclk_ratios = NULL; + break; + case 22579200: + mclkdiv2 = 1; + /* fallthru */ + case 11289600: + es8388->sysclk_constraints = &constraints_11289; + es8388->mclk_ratios = ratios_11289; + break; + case 24576000: + mclkdiv2 = 1; + /* fallthru */ + case 12288000: + es8388->sysclk_constraints = &constraints_12288; + es8388->mclk_ratios = ratios_12288; + break; + default: + return -EINVAL; + } + + es8388->mclkdiv2 = mclkdiv2; + return 0; +} + +static int es8388_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct es8388_priv *es8388 = snd_soc_component_get_drvdata(component); + u8 dac_mode = 0; + u8 adc_mode = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + /* Master serial port mode, with BCLK generated automatically */ + snd_soc_component_update_bits(component, ES8388_MASTERMODE, + ES8388_MASTERMODE_MSC, + ES8388_MASTERMODE_MSC); + es8388->master = true; + break; + case SND_SOC_DAIFMT_CBS_CFS: + /* Slave serial port mode */ + snd_soc_component_update_bits(component, ES8388_MASTERMODE, + ES8388_MASTERMODE_MSC, 0); + es8388->master = false; + break; + default: + return -EINVAL; + } + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + dac_mode |= ES8388_DACCONTROL1_DACFORMAT_I2S; + adc_mode |= ES8388_ADCCONTROL4_ADCFORMAT_I2S; + break; + case SND_SOC_DAIFMT_RIGHT_J: + dac_mode |= ES8388_DACCONTROL1_DACFORMAT_RJUST; + adc_mode |= ES8388_ADCCONTROL4_ADCFORMAT_RJUST; + break; + case SND_SOC_DAIFMT_LEFT_J: + dac_mode |= ES8388_DACCONTROL1_DACFORMAT_LJUST; + adc_mode |= ES8388_ADCCONTROL4_ADCFORMAT_LJUST; + break; + default: + return -EINVAL; + } + + /* clock inversion */ + if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF) + return -EINVAL; + + snd_soc_component_update_bits(component, ES8388_DACCONTROL1, + ES8388_DACCONTROL1_DACFORMAT_MASK, dac_mode); + snd_soc_component_update_bits(component, ES8388_ADCCONTROL4, + ES8388_ADCCONTROL4_ADCFORMAT_MASK, adc_mode); + + return 0; +} + +static int es8388_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + switch (level) { + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + /* VREF, VMID=2x50k, digital enabled */ + snd_soc_component_write(component, ES8388_CHIPPOWER, 0); + snd_soc_component_update_bits(component, ES8388_CONTROL1, + ES8388_CONTROL1_VMIDSEL_MASK | + ES8388_CONTROL1_ENREF, + ES8388_CONTROL1_VMIDSEL_50k | + ES8388_CONTROL1_ENREF); + break; + + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + snd_soc_component_update_bits(component, ES8388_CONTROL1, + ES8388_CONTROL1_VMIDSEL_MASK | + ES8388_CONTROL1_ENREF, + ES8388_CONTROL1_VMIDSEL_5k | + ES8388_CONTROL1_ENREF); + + /* Charge caps */ + msleep(100); + } + + snd_soc_component_write(component, ES8388_CONTROL2, + ES8388_CONTROL2_OVERCURRENT_ON | + ES8388_CONTROL2_THERMAL_SHUTDOWN_ON); + + /* VREF, VMID=2*500k, digital stopped */ + snd_soc_component_update_bits(component, ES8388_CONTROL1, + ES8388_CONTROL1_VMIDSEL_MASK | + ES8388_CONTROL1_ENREF, + ES8388_CONTROL1_VMIDSEL_500k | + ES8388_CONTROL1_ENREF); + break; + + case SND_SOC_BIAS_OFF: + snd_soc_component_update_bits(component, ES8388_CONTROL1, + ES8388_CONTROL1_VMIDSEL_MASK | + ES8388_CONTROL1_ENREF, + 0); + break; + } + return 0; +} + +static const struct snd_soc_dai_ops es8388_dai_ops = { + .startup = es8388_startup, + .hw_params = es8388_hw_params, + .mute_stream = es8388_mute, + .set_sysclk = es8388_set_sysclk, + .set_fmt = es8388_set_dai_fmt, +}; + +static struct snd_soc_dai_driver es8388_dai = { + .name = "es8388-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = ES8388_RATES, + .formats = ES8388_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = ES8388_RATES, + .formats = ES8388_FORMATS, + }, + .ops = &es8388_dai_ops, + .symmetric_rates = 1, +}; + +static int es8388_suspend(struct snd_soc_component *component) +{ + return 0; +} + +static int es8388_resume(struct snd_soc_component *component) +{ + struct regmap *regmap = dev_get_regmap(component->dev, NULL); + struct es8388_priv *es8388; + int ret; + + es8388 = snd_soc_component_get_drvdata(component); + + regcache_mark_dirty(regmap); + ret = regcache_sync(regmap); + if (ret) { + dev_err(component->dev, "unable to sync regcache\n"); + return ret; + } + + return 0; +} + +static int es8388_component_probe(struct snd_soc_component *component) +{ + snd_soc_component_write(component, ES8388_ADCPOWER, 0xf0); + snd_soc_component_write(component, ES8388_CONTROL1, 0x30); + snd_soc_component_write(component, ES8388_DACCONTROL21, 0x80); + snd_soc_component_write(component, ES8388_ADCCONTROL10, 0xda); + + return 0; +} + +static void es8388_remove(struct snd_soc_component *component) +{ +} + +const struct regmap_config es8388_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = ES8388_REG_MAX, + .cache_type = REGCACHE_RBTREE, + .use_single_read = true, + .use_single_write = true, +}; +EXPORT_SYMBOL_GPL(es8388_regmap_config); + +static const struct snd_soc_component_driver es8388_component_driver = { + .probe = es8388_component_probe, + .remove = es8388_remove, + .suspend = es8388_suspend, + .resume = es8388_resume, + .set_bias_level = es8388_set_bias_level, + .controls = es8388_snd_controls, + .num_controls = ARRAY_SIZE(es8388_snd_controls), + .dapm_widgets = es8388_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(es8388_dapm_widgets), + .dapm_routes = es8388_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(es8388_dapm_routes), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, + .non_legacy_dai_naming = 1, +}; + +int es8388_probe(struct device *dev, struct regmap *regmap) +{ + struct es8388_priv *es8388; + + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + es8388 = devm_kzalloc(dev, sizeof(*es8388), GFP_KERNEL); + if (es8388 == NULL) + return -ENOMEM; + + es8388->regmap = regmap; + + dev_set_drvdata(dev, es8388); + + return devm_snd_soc_register_component(dev, + &es8388_component_driver, &es8388_dai, 1); +} +EXPORT_SYMBOL_GPL(es8388_probe); + +static const struct i2c_device_id es8388_id[] = { + { "es8388", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, es8388_id); + +static const struct of_device_id es8388_of_match[] = { + { .compatible = "everest,es8388", }, + { } +}; +MODULE_DEVICE_TABLE(of, es8388_of_match); + +static struct acpi_device_id es8388_acpi_match[] = { + {"ESSX8388", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, es8388_acpi_match); + +static int es8388_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + return es8388_probe(&i2c->dev, + devm_regmap_init_i2c(i2c, &es8388_regmap_config)); +} + +static struct i2c_driver es8388_i2c_driver = { + .driver = { + .name = "es8388", + .of_match_table = es8388_of_match, + .acpi_match_table = es8388_acpi_match, + }, + .probe = es8388_i2c_probe, + .id_table = es8388_id, +}; + +module_i2c_driver(es8388_i2c_driver); + +MODULE_DESCRIPTION("ASoC ES8388 driver"); +MODULE_AUTHOR("Yiqun Zhang "); +MODULE_LICENSE("GPL"); diff --git a/target/linux/phytium/files-5.10/sound/soc/codecs/es8388.h b/target/linux/phytium/files-5.10/sound/soc/codecs/es8388.h new file mode 100644 index 000000000..5858a7126 --- /dev/null +++ b/target/linux/phytium/files-5.10/sound/soc/codecs/es8388.h @@ -0,0 +1,290 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * es8388.h -- ES8388 ALSA SoC Audio driver + */ + +#ifndef _ES8388_H +#define _ES8388_H + +#include + +struct device; + +extern const struct regmap_config es8388_regmap_config; +int es8388_probe(struct device *dev, struct regmap *regmap); + +#define ES8388_DACLVOL 46 +#define ES8388_DACRVOL 47 +#define ES8388_DACCTL 28 +#define ES8388_RATEMASK (0x1f << 0) + +#define ES8388_CONTROL1 0x00 +#define ES8388_CONTROL1_VMIDSEL_OFF (0 << 0) +#define ES8388_CONTROL1_VMIDSEL_50k (1 << 0) +#define ES8388_CONTROL1_VMIDSEL_500k (2 << 0) +#define ES8388_CONTROL1_VMIDSEL_5k (3 << 0) +#define ES8388_CONTROL1_VMIDSEL_MASK (3 << 0) +#define ES8388_CONTROL1_ENREF (1 << 2) +#define ES8388_CONTROL1_SEQEN (1 << 3) +#define ES8388_CONTROL1_SAMEFS (1 << 4) +#define ES8388_CONTROL1_DACMCLK_ADC (0 << 5) +#define ES8388_CONTROL1_DACMCLK_DAC (1 << 5) +#define ES8388_CONTROL1_LRCM (1 << 6) +#define ES8388_CONTROL1_SCP_RESET (1 << 7) + +#define ES8388_CONTROL2 0x01 +#define ES8388_CONTROL2_VREF_BUF_OFF (1 << 0) +#define ES8388_CONTROL2_VREF_LOWPOWER (1 << 1) +#define ES8388_CONTROL2_IBIASGEN_OFF (1 << 2) +#define ES8388_CONTROL2_ANALOG_OFF (1 << 3) +#define ES8388_CONTROL2_VREF_BUF_LOWPOWER (1 << 4) +#define ES8388_CONTROL2_VCM_MOD_LOWPOWER (1 << 5) +#define ES8388_CONTROL2_OVERCURRENT_ON (1 << 6) +#define ES8388_CONTROL2_THERMAL_SHUTDOWN_ON (1 << 7) + +#define ES8388_CHIPPOWER 0x02 +#define ES8388_CHIPPOWER_DACVREF_OFF 0 +#define ES8388_CHIPPOWER_ADCVREF_OFF 1 +#define ES8388_CHIPPOWER_DACDLL_OFF 2 +#define ES8388_CHIPPOWER_ADCDLL_OFF 3 +#define ES8388_CHIPPOWER_DACSTM_RESET 4 +#define ES8388_CHIPPOWER_ADCSTM_RESET 5 +#define ES8388_CHIPPOWER_DACDIG_OFF 6 +#define ES8388_CHIPPOWER_ADCDIG_OFF 7 + +#define ES8388_ADCPOWER 0x03 +#define ES8388_ADCPOWER_INT1_LOWPOWER 0 +#define ES8388_ADCPOWER_FLASH_ADC_LOWPOWER 1 +#define ES8388_ADCPOWER_ADC_BIAS_GEN_OFF 2 +#define ES8388_ADCPOWER_MIC_BIAS_OFF 3 +#define ES8388_ADCPOWER_ADCR_OFF 4 +#define ES8388_ADCPOWER_ADCL_OFF 5 +#define ES8388_ADCPOWER_AINR_OFF 6 +#define ES8388_ADCPOWER_AINL_OFF 7 + +#define ES8388_DACPOWER 0x04 +#define ES8388_DACPOWER_OUT3_ON 0 +#define ES8388_DACPOWER_MONO_ON 1 +#define ES8388_DACPOWER_ROUT2_ON 2 +#define ES8388_DACPOWER_LOUT2_ON 3 +#define ES8388_DACPOWER_ROUT1_ON 4 +#define ES8388_DACPOWER_LOUT1_ON 5 +#define ES8388_DACPOWER_RDAC_OFF 6 +#define ES8388_DACPOWER_LDAC_OFF 7 + +#define ES8388_CHIPLOPOW1 0x05 +#define ES8388_CHIPLOPOW2 0x06 +#define ES8388_ANAVOLMANAG 0x07 + +#define ES8388_MASTERMODE 0x08 +#define ES8388_MASTERMODE_BCLKDIV (0 << 0) +#define ES8388_MASTERMODE_BCLK_INV (1 << 5) +#define ES8388_MASTERMODE_MCLKDIV2 (1 << 6) +#define ES8388_MASTERMODE_MSC (1 << 7) + +#define ES8388_ADCCONTROL1 0x09 +#define ES8388_ADCCONTROL2 0x0a +#define ES8388_ADCCONTROL3 0x0b + +#define ES8388_ADCCONTROL4 0x0c +#define ES8388_ADCCONTROL4_ADCFORMAT_MASK (3 << 0) +#define ES8388_ADCCONTROL4_ADCFORMAT_I2S (0 << 0) +#define ES8388_ADCCONTROL4_ADCFORMAT_LJUST (1 << 0) +#define ES8388_ADCCONTROL4_ADCFORMAT_RJUST (2 << 0) +#define ES8388_ADCCONTROL4_ADCFORMAT_PCM (3 << 0) +#define ES8388_ADCCONTROL4_ADCWL_SHIFT 2 +#define ES8388_ADCCONTROL4_ADCWL_MASK (7 << 2) +#define ES8388_ADCCONTROL4_ADCLRP_I2S_POL_NORMAL (0 << 5) +#define ES8388_ADCCONTROL4_ADCLRP_I2S_POL_INV (1 << 5) +#define ES8388_ADCCONTROL4_ADCLRP_PCM_MSB_CLK2 (0 << 5) +#define ES8388_ADCCONTROL4_ADCLRP_PCM_MSB_CLK1 (1 << 5) + +#define ES8388_ADCCONTROL5 0x0d +#define ES8388_ADCCONTROL5_RATEMASK (0x1f << 0) + +#define ES8388_ADCCONTROL6 0x0e + +#define ES8388_ADCCONTROL7 0x0f +#define ES8388_ADCCONTROL7_ADC_MUTE (1 << 2) +#define ES8388_ADCCONTROL7_ADC_LER (1 << 3) +#define ES8388_ADCCONTROL7_ADC_ZERO_CROSS (1 << 4) +#define ES8388_ADCCONTROL7_ADC_SOFT_RAMP (1 << 5) +#define ES8388_ADCCONTROL7_ADC_RAMP_RATE_4 (0 << 6) +#define ES8388_ADCCONTROL7_ADC_RAMP_RATE_8 (1 << 6) +#define ES8388_ADCCONTROL7_ADC_RAMP_RATE_16 (2 << 6) +#define ES8388_ADCCONTROL7_ADC_RAMP_RATE_32 (3 << 6) + +#define ES8388_ADCCONTROL8 0x10 +#define ES8388_ADCCONTROL9 0x11 +#define ES8388_ADCCONTROL10 0x12 +#define ES8388_ADCCONTROL11 0x13 +#define ES8388_ADCCONTROL12 0x14 +#define ES8388_ADCCONTROL13 0x15 +#define ES8388_ADCCONTROL14 0x16 + +#define ES8388_DACCONTROL1 0x17 +#define ES8388_DACCONTROL1_DACFORMAT_MASK (3 << 1) +#define ES8388_DACCONTROL1_DACFORMAT_I2S (0 << 1) +#define ES8388_DACCONTROL1_DACFORMAT_LJUST (1 << 1) +#define ES8388_DACCONTROL1_DACFORMAT_RJUST (2 << 1) +#define ES8388_DACCONTROL1_DACFORMAT_PCM (3 << 1) +#define ES8388_DACCONTROL1_DACWL_SHIFT 3 +#define ES8388_DACCONTROL1_DACWL_MASK (7 << 3) +#define ES8388_DACCONTROL1_DACLRP_I2S_POL_NORMAL (0 << 6) +#define ES8388_DACCONTROL1_DACLRP_I2S_POL_INV (1 << 6) +#define ES8388_DACCONTROL1_DACLRP_PCM_MSB_CLK2 (0 << 6) +#define ES8388_DACCONTROL1_DACLRP_PCM_MSB_CLK1 (1 << 6) +#define ES8388_DACCONTROL1_LRSWAP (1 << 7) + +#define ES8388_DACCONTROL2 0x18 +#define ES8388_DACCONTROL2_RATEMASK (0x1f << 0) +#define ES8388_DACCONTROL2_DOUBLESPEED (1 << 5) + +#define ES8388_DACCONTROL3 0x19 +#define ES8388_DACCONTROL3_AUTOMUTE (1 << 2) +#define ES8388_DACCONTROL3_DACMUTE (1 << 2) +#define ES8388_DACCONTROL3_LEFTGAINVOL (1 << 3) +#define ES8388_DACCONTROL3_DACZEROCROSS (1 << 4) +#define ES8388_DACCONTROL3_DACSOFTRAMP (1 << 5) +#define ES8388_DACCONTROL3_DACRAMPRATE (3 << 6) + +#define ES8388_LDACVOL 0x1a +#define ES8388_LDACVOL_MASK (0 << 0) +#define ES8388_LDACVOL_MAX (0xc0) + +#define ES8388_RDACVOL 0x1b +#define ES8388_RDACVOL_MASK (0 << 0) +#define ES8388_RDACVOL_MAX (0xc0) + +#define ES8388_DACVOL_MAX (0xc0) + +#define ES8388_DACCONTROL4 0x1a +#define ES8388_DACCONTROL5 0x1b + +#define ES8388_DACCONTROL6 0x1c +#define ES8388_DACCONTROL6_CLICKFREE (1 << 3) +#define ES8388_DACCONTROL6_DAC_INVR (1 << 4) +#define ES8388_DACCONTROL6_DAC_INVL (1 << 5) +#define ES8388_DACCONTROL6_DEEMPH_MASK (3 << 6) +#define ES8388_DACCONTROL6_DEEMPH_OFF (0 << 6) +#define ES8388_DACCONTROL6_DEEMPH_32k (1 << 6) +#define ES8388_DACCONTROL6_DEEMPH_44_1k (2 << 6) +#define ES8388_DACCONTROL6_DEEMPH_48k (3 << 6) + +#define ES8388_DACCONTROL7 0x1d +#define ES8388_DACCONTROL7_VPP_SCALE_3p5 (0 << 0) +#define ES8388_DACCONTROL7_VPP_SCALE_4p0 (1 << 0) +#define ES8388_DACCONTROL7_VPP_SCALE_3p0 (2 << 0) +#define ES8388_DACCONTROL7_VPP_SCALE_2p5 (3 << 0) +#define ES8388_DACCONTROL7_SHELVING_STRENGTH (1 << 2) /* In eights */ +#define ES8388_DACCONTROL7_MONO (1 << 5) +#define ES8388_DACCONTROL7_ZEROR (1 << 6) +#define ES8388_DACCONTROL7_ZEROL (1 << 7) + +/* Shelving filter */ +#define ES8388_DACCONTROL8 0x1e +#define ES8388_DACCONTROL9 0x1f +#define ES8388_DACCONTROL10 0x20 +#define ES8388_DACCONTROL11 0x21 +#define ES8388_DACCONTROL12 0x22 +#define ES8388_DACCONTROL13 0x23 +#define ES8388_DACCONTROL14 0x24 +#define ES8388_DACCONTROL15 0x25 + +#define ES8388_DACCONTROL16 0x26 +#define ES8388_DACCONTROL16_RMIXSEL_RIN1 (0 << 0) +#define ES8388_DACCONTROL16_RMIXSEL_RIN2 (1 << 0) +#define ES8388_DACCONTROL16_RMIXSEL_RIN3 (2 << 0) +#define ES8388_DACCONTROL16_RMIXSEL_RADC (3 << 0) +#define ES8388_DACCONTROL16_LMIXSEL_LIN1 (0 << 3) +#define ES8388_DACCONTROL16_LMIXSEL_LIN2 (1 << 3) +#define ES8388_DACCONTROL16_LMIXSEL_LIN3 (2 << 3) +#define ES8388_DACCONTROL16_LMIXSEL_LADC (3 << 3) + +#define ES8388_DACCONTROL17 0x27 +#define ES8388_DACCONTROL17_LI2LOVOL (7 << 3) +#define ES8388_DACCONTROL17_LI2LO (1 << 6) +#define ES8388_DACCONTROL17_LD2LO (1 << 7) + +#define ES8388_DACCONTROL18 0x28 +#define ES8388_DACCONTROL18_RI2LOVOL (7 << 3) +#define ES8388_DACCONTROL18_RI2LO (1 << 6) +#define ES8388_DACCONTROL18_RD2LO (1 << 7) + +#define ES8388_DACCONTROL19 0x29 +#define ES8388_DACCONTROL19_LI2ROVOL (7 << 3) +#define ES8388_DACCONTROL19_LI2RO (1 << 6) +#define ES8388_DACCONTROL19_LD2RO (1 << 7) + +#define ES8388_DACCONTROL20 0x2a +#define ES8388_DACCONTROL20_RI2ROVOL (7 << 3) +#define ES8388_DACCONTROL20_RI2RO (1 << 6) +#define ES8388_DACCONTROL20_RD2RO (1 << 7) + +#define ES8388_DACCONTROL21 0x2b +#define ES8388_DACCONTROL21_LI2MOVOL (7 << 3) +#define ES8388_DACCONTROL21_LI2MO (1 << 6) +#define ES8388_DACCONTROL21_LD2MO (1 << 7) + +#define ES8388_DACCONTROL22 0x2c +#define ES8388_DACCONTROL22_RI2MOVOL (7 << 3) +#define ES8388_DACCONTROL22_RI2MO (1 << 6) +#define ES8388_DACCONTROL22_RD2MO (1 << 7) + +#define ES8388_DACCONTROL23 0x2d +#define ES8388_DACCONTROL23_MOUTINV (1 << 1) +#define ES8388_DACCONTROL23_HPSWPOL (1 << 2) +#define ES8388_DACCONTROL23_HPSWEN (1 << 3) +#define ES8388_DACCONTROL23_VROI_1p5k (0 << 4) +#define ES8388_DACCONTROL23_VROI_40k (1 << 4) +#define ES8388_DACCONTROL23_OUT3_VREF (0 << 5) +#define ES8388_DACCONTROL23_OUT3_ROUT1 (1 << 5) +#define ES8388_DACCONTROL23_OUT3_MONOOUT (2 << 5) +#define ES8388_DACCONTROL23_OUT3_RIGHT_MIXER (3 << 5) +#define ES8388_DACCONTROL23_ROUT2INV (1 << 7) + +/* LOUT1 Amplifier */ +#define ES8388_LOUT1VOL 0x2e +#define ES8388_LOUT1VOL_MASK (0 << 5) +#define ES8388_LOUT1VOL_MAX (0x24) + +/* ROUT1 Amplifier */ +#define ES8388_ROUT1VOL 0x2f +#define ES8388_ROUT1VOL_MASK (0 << 5) +#define ES8388_ROUT1VOL_MAX (0x24) + +#define ES8388_OUT1VOL_MAX (0x24) + +/* LOUT2 Amplifier */ +#define ES8388_LOUT2VOL 0x30 +#define ES8388_LOUT2VOL_MASK (0 << 5) +#define ES8388_LOUT2VOL_MAX (0x24) + +/* ROUT2 Amplifier */ +#define ES8388_ROUT2VOL 0x31 +#define ES8388_ROUT2VOL_MASK (0 << 5) +#define ES8388_ROUT2VOL_MAX (0x24) + +#define ES8388_OUT2VOL_MAX (0x24) + +/* Mono Out Amplifier */ +#define ES8388_MONOOUTVOL 0x32 +#define ES8388_MONOOUTVOL_MASK (0 << 5) +#define ES8388_MONOOUTVOL_MAX (0x24) + +#define ES8388_DACCONTROL29 0x33 +#define ES8388_DACCONTROL30 0x34 + +#define ES8388_SYSCLK 0 + +#define ES8388_REG_MAX 0x35 + +#define ES8388_1536FS 1536 +#define ES8388_1024FS 1024 +#define ES8388_768FS 768 +#define ES8388_512FS 512 +#define ES8388_384FS 384 +#define ES8388_256FS 256 +#define ES8388_128FS 128 + +#endif diff --git a/target/linux/phytium/files-5.10/sound/soc/phytium/Kconfig b/target/linux/phytium/files-5.10/sound/soc/phytium/Kconfig new file mode 100755 index 000000000..8769ed7cc --- /dev/null +++ b/target/linux/phytium/files-5.10/sound/soc/phytium/Kconfig @@ -0,0 +1,30 @@ +config SND_SOC_PHYTIUM_I2S + tristate "Phytium I2S Device Driver" + help + Say Y or M if you want to add support for I2S driver for + Phytium I2S device . The device supports 2 channels each + for play and record. + +config SND_PMDK_ES8388 + tristate "Phytium machine support with ES8388" + depends on I2C && SND_SOC_PHYTIUM_I2S + select SND_SOC_ES8388 + help + Say Y if you want to add Phytium machine support for + ES8388 codecs. + +config SND_PMDK_ES8336 + tristate "Phytium machine support with ES8336" + depends on I2C && SND_SOC_PHYTIUM_I2S + select SND_SOC_ES8336 + help + Say Y if you want to add Phytium machine support for + ES8336 codecs. + +config SND_PMDK_DP + tristate "Phytium machine support with DP" + depends on I2C && SND_SOC_PHYTIUM_I2S + select SND_SOC_HDMI_CODEC + help + Say Y if you want to add Phytium machine support for + Displayport. diff --git a/target/linux/phytium/files-5.10/sound/soc/phytium/Makefile b/target/linux/phytium/files-5.10/sound/soc/phytium/Makefile new file mode 100644 index 000000000..db3c0659e --- /dev/null +++ b/target/linux/phytium/files-5.10/sound/soc/phytium/Makefile @@ -0,0 +1,13 @@ +# PHYTIUM Platform Support + +snd-soc-phytium-i2s-objs :=phytium_i2s.o +obj-$(CONFIG_SND_SOC_PHYTIUM_I2S) += snd-soc-phytium-i2s.o + +snd-soc-pmdk-es8388-objs :=pmdk_es8388.o +obj-$(CONFIG_SND_PMDK_ES8388) += snd-soc-pmdk-es8388.o + +snd-soc-pmdk-es8336-objs :=pmdk_es8336.o +obj-$(CONFIG_SND_PMDK_ES8336) += snd-soc-pmdk-es8336.o + +snd-soc-pmdk-dp-objs :=pmdk_dp.o +obj-$(CONFIG_SND_PMDK_DP) += snd-soc-pmdk-dp.o diff --git a/target/linux/phytium/files-5.10/sound/soc/phytium/local.h b/target/linux/phytium/files-5.10/sound/soc/phytium/local.h new file mode 100644 index 000000000..3076a9588 --- /dev/null +++ b/target/linux/phytium/files-5.10/sound/soc/phytium/local.h @@ -0,0 +1,328 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2020-2023 Phytium Technology Co., Ltd. + */ + +#ifndef __PHYTIUM_I2S_LOCAL_H +#define __PHYTIUM_I2S_LOCAL_H + +#include +#include +#include +#include + +/* I2S clk setting*/ +#define CLK_CFG0 0xc00 +#define CLK_CFG1 0xc04 + +/* common register for all channel */ +#define I2S_IER 0x000 +#define IRER 0x004 +#define ITER 0x008 +#define CER 0x00C + +#define RXFFR 0x014 +#define TXFFR 0x018 + +/* Interrupt status register fields */ +#define ISR_TXFO BIT(5) +#define ISR_TXFE BIT(4) +#define ISR_RXFO BIT(1) +#define ISR_RXDA BIT(0) + +/* I2STxRxRegisters for all channels */ +#define LRBR_LTHR(x) (0x40 * x + 0x020) +#define RRBR_RTHR(x) (0x40 * x + 0x024) +#define RER(x) (0x40 * x + 0x028) + +#define RCR(x) (0x40 * x + 0x030) + +#define ISR(x) (0x40 * x + 0x038) +#define IMR(x) (0x40 * x + 0x03C) +#define ROR(x) (0x40 * x + 0x040) +#define TOR(x) (0x40 * x + 0x044) +#define RFCR(x) (0x40 * x + 0x048) +#define TFCR(x) (0x40 * x + 0x04C) +#define RFF(x) (0x40 * x + 0x050) +#define TFF(x) (0x40 * x + 0x054) + +/*enable txd and rxd block channel0~3 */ +#define TER(x) (0x40 * x + 0x02C) +#define CCR 0x010 +#define TCR(x) (0x40 * x + 0x034) + + +/* I2SCOMPRegisters */ +#define I2S_COMP_PARAM_2 0x01F0 +#define I2S_COMP_PARAM_1 0x01F4 +#define I2S_COMP_VERSION 0x01F8 +#define I2S_COMP_TYPE 0x01FC + +/***I2S AND DMA***/ + +#define DMA_GCAP 0x0024 + +#define DMA_CHAL_CONFG1 0x0028 + +#define DMA_CHAL_CONFG0 0x0004 +#define DMA_MASK_INT 0x000c +#define DMA_BDLPU(x) (0x40 * x + 0x0040) +#define DMA_BDLPL(x) (0x40 * x + 0x0044) +#define DMA_CHALX_DEV_ADDR(x) (0x40 * x + 0x0048) +#define DMA_CHALX_CBL(x) (0x40 * x + 0x0054) +#define DMA_CHALX_LVI(x) (0x40 * x + 0x004c) + +#define DMA_CHALX_DSIZE(x) (0x40 * x + 0x0064) +#define DMA_CHALX_DLENTH(x) (0x40 * x + 0x0068) +#define DMA_CHALX_CTL(x) (0x40 * x + 0x0058) + + +#define DMA_CTL 0x0000 + +#define DMA_LPIB(x) (0x40 * x + 0x0050) + +#define DMA_STS 0x0008 + +/****************/ + + +/* max number of fragments - we may use more if allocating more pages for BDL */ +#define BDL_SIZE 4096 +#define AZX_MAX_BDL_ENTRIES (BDL_SIZE / 16) + +/* + * Component parameter register fields - define the I2S block's + * configuration. + */ +#define COMP1_TX_WORDSIZE_3(r) (((r) & GENMASK(27, 25)) >> 25) +#define COMP1_TX_WORDSIZE_2(r) (((r) & GENMASK(24, 22)) >> 22) +#define COMP1_TX_WORDSIZE_1(r) (((r) & GENMASK(21, 19)) >> 19) +#define COMP1_TX_WORDSIZE_0(r) (((r) & GENMASK(18, 16)) >> 16) +#define COMP1_TX_CHANNELS(r) (((r) & GENMASK(10, 9)) >> 9) +#define COMP1_RX_CHANNELS(r) (((r) & GENMASK(8, 7)) >> 7) +#define COMP1_RX_ENABLED(r) (((r) & BIT(6)) >> 6) +#define COMP1_TX_ENABLED(r) (((r) & BIT(5)) >> 5) +#define COMP1_MODE_EN(r) (((r) & BIT(4)) >> 4) +#define COMP1_FIFO_DEPTH_GLOBAL(r) (((r) & GENMASK(3, 2)) >> 2) +#define COMP1_APB_DATA_WIDTH(r) (((r) & GENMASK(1, 0)) >> 0) + +#define COMP2_RX_WORDSIZE_3(r) (((r) & GENMASK(12, 10)) >> 10) +#define COMP2_RX_WORDSIZE_2(r) (((r) & GENMASK(9, 7)) >> 7) +#define COMP2_RX_WORDSIZE_1(r) (((r) & GENMASK(5, 3)) >> 3) +#define COMP2_RX_WORDSIZE_0(r) (((r) & GENMASK(2, 0)) >> 0) + +/* Number of entries in WORDSIZE and DATA_WIDTH parameter registers */ +#define COMP_MAX_WORDSIZE (1 << 3) +#define COMP_MAX_DATA_WIDTH (1 << 2) + +#define MAX_CHANNEL_NUM 8 +#define MIN_CHANNEL_NUM 2 + +#define azx_bus(chip) (&(chip)->bus.core) +#define bus_to_azx(_bus) container_of(_bus, struct azx, bus.core) + +#define I2S_UNSOL_QUEUE_SIZE 64 +#define I2S_MAX_CODECS 8 /* limit by controller side */ + + +#define azx_stream(dev) (&(dev)->core) + +struct i2s_clk_config_data { + int chan_nr; + u32 data_width; + u32 sample_rate; +}; + +struct i2sc_bus { + struct device *dev; + const struct i2s_bus_ops *ops; + const struct i2s_io_ops *io_ops; + const struct i2s_ext_bus_ops *ext_ops; + + /* h/w resources */ + unsigned long addr; + void __iomem *remap_addr; + int irq; + + /* codec linked list */ + struct list_head codec_list; + unsigned int num_codecs; + + unsigned int unsol_rp, unsol_wp; + struct work_struct unsol_work; + + struct snd_dma_buffer bdl0; + struct snd_dma_buffer bdl1; + + /* i2s_stream linked list */ + struct list_head stream_list; + + bool reverse_assign; /* assign devices in reverse order */ + + int bdl_pos_adj; /* BDL position adjustment */ + + /* locks */ + spinlock_t reg_lock; +}; + +struct i2s_bus { + struct i2sc_bus core; + + struct snd_card *card; + + struct pci_dev *pci; + + struct mutex prepare_mutex; +}; + + +/* + * i2s stream + */ +struct i2s_stream { + struct i2sc_bus *bus; + struct snd_dma_buffer bdl; /* BDL buffer */ + __le32 *posbuf; /* position buffer pointer */ + int direction; /* playback / capture (SNDRV_PCM_STREAM_*) */ + + unsigned int bufsize; /* size of the play buffer in bytes */ + unsigned int period_bytes; /* size of the period in bytes */ + unsigned int frags; /* number for period in the play buffer */ + unsigned int fifo_size; /* FIFO size */ + + void __iomem *sd_addr; /* stream descriptor pointer */ + + u32 sd_int_sta_mask; /* stream int status mask */ + + /* pcm support */ + struct snd_pcm_substream *substream; /* assigned substream, + * set in PCM open + */ + unsigned int format_val; /* format value to be set in the + * controller and the codec + */ + unsigned char stream_tag; /* assigned stream */ + unsigned char index; /* stream index */ + int assigned_key; /* last device# key assigned to */ + + bool opened; + bool running; + bool prepared; + bool no_period_wakeup; + + int delay_negative_threshold; + + struct list_head list; + +}; + + +struct azx_dev { + struct i2s_stream core; + unsigned int irq_pending:1; +}; + + + +/* PCM setup */ +static inline struct azx_dev *get_azx_dev(struct snd_pcm_substream *substream) +{ + return substream->runtime->private_data; +} + + +#define AZX_MAX_CODECS HDA_MAX_CODECS +#define AZX_DEFAULT_CODECS 4 + +#define stream_to_azx_dev(s) container_of(s, struct azx_dev, core) + +struct azx; + +struct i2s_controller_ops { + int (*substream_alloc_pages)(struct azx *chip, + struct snd_pcm_substream *substream, + size_t size); + int (*substream_free_pages)(struct azx *chip, + struct snd_pcm_substream *substream); + int (*position_check)(struct azx *chip, struct azx_dev *azx_dev); +}; + +struct i2s_io_ops { + int (*dma_alloc_pages)(struct i2sc_bus *bus, int type, size_t size, + struct snd_dma_buffer *buf); + void (*dma_free_pages)(struct i2sc_bus *bus, + struct snd_dma_buffer *buf); +}; + +struct azx { + struct i2s_bus bus; + + struct snd_card *card; + struct pci_dev *pci; + int dev_index; + + int playback_streams; + int playback_index_offset; + int capture_streams; + int capture_index_offset; + int num_streams; + + /* Register interaction. */ + const struct i2s_controller_ops *ops; + + /* locks */ + struct mutex open_mutex; /* Prevents concurrent open/close operations */ + + /* PCM */ + struct list_head pcm_list; /* azx_pcm list */ + + /* flags */ + int bdl_pos_adj; + unsigned int running:1; + unsigned int region_requested:1; + unsigned int disabled:1; +}; +struct i2s_phytium { + struct azx chip; + struct snd_pcm_substream *substream; + struct device *dev; + struct device *pdev; + u32 paddr; + void __iomem *regs; + void __iomem *regs_db; + int irq_id; + + /* for pending irqs */ + struct work_struct irq_pending_work; + + /* sync probing */ + struct completion probe_wait; + struct work_struct probe_work; + + /* extra flags */ + unsigned int pcie:1; + unsigned int irq_pending_warned:1; + unsigned int probe_continued:1; + unsigned int i2s_dp:1; + + unsigned int i2s_reg_comp1; + unsigned int i2s_reg_comp2; + struct clk *clk; + unsigned int capability; + unsigned int quirks; + u32 fifo_th; + int active; + u32 xfer_resolution; + u32 ccr; + u32 clk_base; + + struct i2s_clk_config_data config; + + /*azx_dev*/ + struct i2s_stream core; +}; + +#define azx_alloc_stream_pages(chip) \ + snd_i2s_bus_alloc_stream_pages(azx_bus(chip)) + +#endif diff --git a/target/linux/phytium/files-5.10/sound/soc/phytium/phytium_i2s.c b/target/linux/phytium/files-5.10/sound/soc/phytium/phytium_i2s.c new file mode 100755 index 000000000..2d028d36b --- /dev/null +++ b/target/linux/phytium/files-5.10/sound/soc/phytium/phytium_i2s.c @@ -0,0 +1,1424 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Phytium I2S ASoC driver + * + * Copyright (c) 2020-2023 Phytium Technology Co., Ltd. + * + * Derived from sound/soc/dwc/dwc-i2s.c + * Copyright (C) 2010 ST Microelectronics + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "local.h" + +#define NUM_CAPTURE 1 +#define NUM_PLAYBACK 1 + +#define PHYTIUM_I2S_PLAY (1 << 0) +#define PHYTIUM_I2S_RECORD (1 << 1) +#define PHYTIUM_I2S_SLAVE (1 << 2) +#define PHYTIUM_I2S_MASTER (1 << 3) + +#define PHYTIUM_I2S_QUIRK_16BIT_IDX_OVERRIDE (1 << 2) + +#define TWO_CHANNEL_SUPPORT 2 /* up to 2.0 */ +#define FOUR_CHANNEL_SUPPORT 4 /* up to 3.1 */ +#define SIX_CHANNEL_SUPPORT 6 /* up to 5.1 */ +#define EIGHT_CHANNEL_SUPPORT 8 /* up to 7.1 */ + +struct pdata_px210_mfd { + struct device *dev; + char *name; + int clk_base; +}; + +static inline void i2s_write_reg(void __iomem *io_base, int reg, u32 val) +{ + writel(val, io_base + reg); +} + +static inline u32 i2s_read_reg(void __iomem *io_base, int reg) +{ + return readl(io_base + reg); +} + +static inline void i2s_disable_channels(struct i2s_phytium *dev, u32 stream) +{ + u32 i = 0; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + for (i = 0; i < 4; i++) + i2s_write_reg(dev->regs, TER(i), 0); + } else { + for (i = 0; i < 4; i++) + i2s_write_reg(dev->regs, RER(i), 0); + } +} + +static int substream_free_pages(struct azx *chip, + struct snd_pcm_substream *substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static void stream_update(struct i2sc_bus *bus, struct i2s_stream *s) +{ + struct azx *chip = bus_to_azx(bus); + + struct azx_dev *azx_dev = stream_to_azx_dev(s); + + /* check whether this IRQ is really acceptable */ + if (!chip->ops->position_check || + chip->ops->position_check(chip, azx_dev)) { + spin_unlock(&bus->reg_lock); + snd_pcm_period_elapsed(azx_stream(azx_dev)->substream); + spin_lock(&bus->reg_lock); + } + +} + +int snd_i2s_bus_handle_stream_irq(struct i2sc_bus *bus, unsigned int status, + void (*ack)(struct i2sc_bus *, + struct i2s_stream *)) +{ + struct i2s_stream *azx_dev; + u32 sd_status, qc_sd_status; + int handled = 0; + + list_for_each_entry(azx_dev, &bus->stream_list, list) { + + if (status & azx_dev->sd_int_sta_mask) { + sd_status = i2s_read_reg(azx_dev->sd_addr, DMA_STS); + i2s_write_reg(azx_dev->sd_addr, DMA_STS, azx_dev->sd_int_sta_mask); + qc_sd_status = i2s_read_reg(azx_dev->sd_addr, DMA_STS); + handled |= 1 << azx_dev->index; + azx_dev->running = 1; + if (!azx_dev->substream || !azx_dev->running || + !(sd_status & 0xffffffff)) { + continue; + } + if (ack) + ack(bus, azx_dev); + } + } + return handled; +} + +irqreturn_t azx_i2s_interrupt(int irq, void *dev_id) +{ + struct azx *chip = dev_id; + struct i2sc_bus *bus = azx_bus(chip); + u32 status; + bool active, handled = false; + int repeat = 0; /* count for avoiding endless loop */ + + spin_lock(&bus->reg_lock); + + if (chip->disabled) + goto unlock; + + do { + + status = i2s_read_reg(bus->remap_addr, DMA_STS); + + if (status == 0) + break; + + handled = true; + active = false; + if (snd_i2s_bus_handle_stream_irq(bus, status, stream_update)) + active = true; + + + } while (active && ++repeat < 1); + + unlock: + spin_unlock(&bus->reg_lock); + + return IRQ_RETVAL(handled); +} + +static int azx_acquire_irq(struct azx *chip, int do_disconnect) +{ + struct i2sc_bus *bus = azx_bus(chip); + struct i2s_phytium *i2s = container_of(chip, struct i2s_phytium, chip); + int err; + + err = devm_request_irq(i2s->dev, i2s->irq_id, azx_i2s_interrupt, IRQF_SHARED, + "phytium i2s", chip); + + if (err < 0) { + dev_err(i2s->dev, "failed to request irq\n"); + return err; + } + + bus->irq = i2s->irq_id; + + return 0; +} + +static void i2s_start(struct i2s_phytium *dev, + struct snd_pcm_substream *substream) +{ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + i2s_write_reg(dev->regs, ITER, 1); + else + i2s_write_reg(dev->regs, IRER, 1); + + /*enable the clock*/ + i2s_write_reg(dev->regs, CER, 1); + + /*enable the i2s*/ + i2s_write_reg(dev->regs, I2S_IER, 1); +} + +static void i2s_stop(struct i2s_phytium *dev, + struct snd_pcm_substream *substream) +{ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + i2s_write_reg(dev->regs, ITER, 0); + else + i2s_write_reg(dev->regs, IRER, 0); + + if (!dev->active) { + i2s_write_reg(dev->regs, CER, 0); + i2s_write_reg(dev->regs, I2S_IER, 0); + } +} + +static void phytium_i2s_config(struct i2s_phytium *dev, int stream) +{ + i2s_disable_channels(dev, stream); + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + i2s_write_reg(dev->regs, TCR(0), dev->xfer_resolution); + i2s_write_reg(dev->regs, TER(0), 1); + } else { + i2s_write_reg(dev->regs, RCR(0), dev->xfer_resolution); + i2s_write_reg(dev->regs, RER(0), 1); + } +} + +static int phytium_i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct i2s_phytium *dev = snd_soc_dai_get_drvdata(dai); + struct i2s_clk_config_data *config = &dev->config; + u64 fix, point; + u32 cfg = 0; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + config->data_width = 16; + dev->ccr = 0x00; + dev->xfer_resolution = 0x02; + break; + + case SNDRV_PCM_FORMAT_S24_LE: + config->data_width = 24; + dev->ccr = 0x08; + dev->xfer_resolution = 0x04; + break; + + case SNDRV_PCM_FORMAT_S32_LE: + config->data_width = 32; + dev->ccr = 0x10; + dev->xfer_resolution = 0x05; + break; + + default: + dev_err(dev->dev, "phytium-i2s: unsupported PCM fmt"); + return -EINVAL; + } + + config->chan_nr = params_channels(params); + + switch (config->chan_nr) { + case EIGHT_CHANNEL_SUPPORT: + case SIX_CHANNEL_SUPPORT: + case FOUR_CHANNEL_SUPPORT: + case TWO_CHANNEL_SUPPORT: + break; + default: + dev_err(dev->dev, "channel not supported\n"); + return -EINVAL; + } + + phytium_i2s_config(dev, substream->stream); + + i2s_write_reg(dev->regs, CCR, dev->ccr); + + config->sample_rate = params_rate(params); + if (dev->capability & PHYTIUM_I2S_MASTER) { + fix = dev->clk_base / config->sample_rate / config->data_width / 32; + point = ((dev->clk_base / config->sample_rate) << 10) / config->data_width / 32; + point = (point - (fix << 10)) * 10; + cfg = ((u16) fix << 16) | (u16) point; + i2s_write_reg(dev->regs, CLK_CFG0, cfg); + i2s_write_reg(dev->regs, CLK_CFG1, 0xf); + } + return 0; +} + +static int phytium_i2s_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct i2s_phytium *dev = snd_soc_dai_get_drvdata(dai); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + i2s_write_reg(dev->regs, TXFFR, 1); + else + i2s_write_reg(dev->regs, RXFFR, 1); + + return 0; +} + +static int phytium_i2s_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + struct i2s_phytium *dev = snd_soc_dai_get_drvdata(dai); + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + dev->active++; + i2s_start(dev, substream); + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + dev->active--; + i2s_stop(dev, substream); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int phytium_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) +{ + struct i2s_phytium *dev = snd_soc_dai_get_drvdata(cpu_dai); + int ret = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + if (dev->capability & PHYTIUM_I2S_SLAVE) + ret = 0; + else + ret = -EINVAL; + break; + case SND_SOC_DAIFMT_CBS_CFS: + if (dev->capability & PHYTIUM_I2S_MASTER) + ret = 0; + else + ret = -EINVAL; + break; + case SND_SOC_DAIFMT_CBM_CFS: + case SND_SOC_DAIFMT_CBS_CFM: + ret = -EINVAL; + break; + default: + dev_dbg(dev->dev, "phytium/i2s: Invalid master/slave format\n"); + ret = -EINVAL; + break; + } + return ret; +} + +static const struct snd_soc_dai_ops phytium_i2s_dai_ops = { + .hw_params = phytium_i2s_hw_params, + .prepare = phytium_i2s_prepare, + .trigger = phytium_i2s_trigger, + .set_fmt = phytium_i2s_set_fmt, +}; + +#ifdef CONFIG_PM + +static int phytium_i2s_suspend(struct snd_soc_component *component) +{ + return 0; +} + +static int phytium_i2s_resume(struct snd_soc_component *component) +{ + struct i2s_phytium *dev = snd_soc_component_get_drvdata(component); + struct snd_soc_dai *dai; + + for_each_component_dais(component, dai) { + if (snd_soc_dai_stream_active(dai, SNDRV_PCM_STREAM_PLAYBACK)) + phytium_i2s_config(dev, SNDRV_PCM_STREAM_PLAYBACK); + if (snd_soc_dai_stream_active(dai, SNDRV_PCM_STREAM_CAPTURE)) + phytium_i2s_config(dev, SNDRV_PCM_STREAM_CAPTURE); + } + + return 0; +} + +#else +#define phytium_i2s_suspend NULL +#define phytium_i2s_resume NULL +#endif + +static struct snd_soc_dai_driver phytium_i2s_dai = { + .playback = { + .stream_name = "i2s-Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S20_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + .capture = { + .stream_name = "i2s-Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S20_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + .ops = &phytium_i2s_dai_ops, + .symmetric_rates = 1, +}; + +static const struct snd_pcm_hardware phytium_pcm_hardware = { + .info = SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_BLOCK_TRANSFER, + .rates = SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000, + .rate_min = 8000, + .rate_max = 48000, + .formats = (SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S20_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 4096*16, + .period_bytes_min = 1024, + .period_bytes_max = 4096*4, + .periods_min = 2, + .periods_max = 16, + .fifo_size = 16, +}; + +struct i2s_stream *snd_i2s_stream_assign(struct i2sc_bus *bus, + struct snd_pcm_substream *substream) +{ + struct i2s_stream *azx_dev; + struct i2s_stream *res = NULL; + + /* make a non-zero unique key for the substream */ + int key = (substream->pcm->device << 16) | (substream->number << 2) | + (substream->stream + 1); + + list_for_each_entry(azx_dev, &bus->stream_list, list) { + if (azx_dev->direction != substream->stream) + continue; + + azx_dev->opened = 0; + + if (azx_dev->assigned_key == key) { + res = azx_dev; + break; + } + + if (!res || bus->reverse_assign) + res = azx_dev; + } + + if (res) { + spin_lock_irq(&bus->reg_lock); + res->opened = 1; + res->running = 0; + res->assigned_key = key; + res->substream = substream; + spin_unlock_irq(&bus->reg_lock); + } + + return res; +} + +/* assign a stream for the PCM */ +static inline struct azx_dev * +azx_assign_device(struct azx *chip, struct snd_pcm_substream *substream) +{ + struct i2s_stream *s; + + s = snd_i2s_stream_assign(azx_bus(chip), substream); + if (!s) + return NULL; + return stream_to_azx_dev(s); +} + +static int phytium_pcm_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct i2s_phytium *dev = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); + struct azx *chip = &dev->chip; + struct azx_dev *azx_dev; + struct snd_pcm_runtime *runtime = substream->runtime; + + azx_dev = azx_assign_device(chip, substream); + if (azx_dev == NULL) + return -EBUSY; + + snd_soc_set_runtime_hwparams(substream, &phytium_pcm_hardware); + snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); + snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 128); + runtime->private_data = azx_dev; + + return 0; +} + +static int phytium_pcm_close(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct i2s_phytium *dev = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); + struct azx *chip = &dev->chip; + struct azx_dev *azx_dev = get_azx_dev(substream); + + mutex_lock(&chip->open_mutex); + azx_stream(azx_dev)->opened = 0; + azx_stream(azx_dev)->running = 0; + azx_stream(azx_dev)->substream = NULL; + + mutex_unlock(&chip->open_mutex); + return 0; +} + +static int phytium_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) +{ + struct i2s_phytium *dev = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); + size_t size = phytium_pcm_hardware.buffer_bytes_max; + + snd_pcm_set_managed_buffer_all(rtd->pcm, + SNDRV_DMA_TYPE_DEV, + dev->pdev, size, size); + + return 0; +} + +static const struct i2s_io_ops axi_i2s_io_ops; +static const struct i2s_controller_ops axi_i2s_ops; + +static int phytium_pcm_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct i2s_phytium *dev = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); + + struct azx *chip = &dev->chip; + struct azx_dev *azx_dev = get_azx_dev(substream); + int ret; + + azx_dev->core.bufsize = 0; + azx_dev->core.period_bytes = 0; + azx_dev->core.format_val = 0; + + ret = chip->ops->substream_alloc_pages(chip, substream, + params_buffer_bytes(hw_params)); + + return ret; +} +/* + * set up a BDL entry + */ +static int setup_bdle(struct i2sc_bus *bus, + struct snd_dma_buffer *dmab, + struct i2s_stream *azx_dev, __le32 **bdlp, + int ofs, int size, int with_ioc) +{ + struct snd_pcm_substream *substream = azx_dev->substream; + struct snd_pcm_runtime *runtime = substream->runtime; + __le32 *bdl = *bdlp; + + dmab->addr = runtime->dma_addr; + while (size > 0) { + dma_addr_t addr; + int chunk; + + if (azx_dev->frags >= AZX_MAX_BDL_ENTRIES) + return -EINVAL; + + addr = snd_sgbuf_get_addr(dmab, ofs); + + /* program the address field of the BDL entry */ + bdl[0] = cpu_to_le32((u32)addr); + + bdl[1] = cpu_to_le32(upper_32_bits(addr)); + + /* program the size field of the BDL entry */ + chunk = snd_sgbuf_get_chunk_size(dmab, ofs, size); + + bdl[2] = cpu_to_le32(chunk); + + /* program the IOC to enable interrupt + * only when the whole fragment is processed + */ + size -= chunk; + bdl[3] = (size || !with_ioc) ? 0 : cpu_to_le32(0x01); + + bdl += 4; + azx_dev->frags++; + ofs += chunk; + } + *bdlp = bdl; + return ofs; +} + +int snd_i2s_stream_setup_periods(struct i2s_stream *azx_dev) +{ + struct i2sc_bus *bus = azx_dev->bus; + struct snd_pcm_substream *substream = azx_dev->substream; + struct snd_pcm_runtime *runtime = substream->runtime; + __le32 *bdl; + int i, ofs, periods, period_bytes; + int pos_adj, pos_align; + + period_bytes = azx_dev->period_bytes; + periods = azx_dev->bufsize / period_bytes; + + /* program the initial BDL entries */ + bdl = (__le32 *)azx_dev->bdl.area; + + ofs = 0; + azx_dev->frags = 0; + + pos_adj = bus->bdl_pos_adj; + + if (!azx_dev->no_period_wakeup && pos_adj > 0) { + + pos_align = pos_adj; + pos_adj = (pos_adj * runtime->rate + 47999) / 48000; + if (!pos_adj) + pos_adj = pos_align; + else + pos_adj = ((pos_adj + pos_align - 1) / pos_align) * + pos_align; + pos_adj = frames_to_bytes(runtime, pos_adj); + if (pos_adj >= period_bytes) { + dev_warn(bus->dev, "Too big adjustment %d\n", + pos_adj); + pos_adj = 0; + } else { + + ofs = setup_bdle(bus, snd_pcm_get_dma_buf(substream), + azx_dev, + &bdl, ofs, pos_adj, true); + if (ofs < 0) + goto error; + } + } else + pos_adj = 0; + + for (i = 0; i < periods; i++) { + if (i == periods - 1 && pos_adj) + ofs = setup_bdle(bus, snd_pcm_get_dma_buf(substream), + azx_dev, &bdl, ofs, + period_bytes - pos_adj, 0); + else + ofs = setup_bdle(bus, snd_pcm_get_dma_buf(substream), + azx_dev, &bdl, ofs, + period_bytes, + !azx_dev->no_period_wakeup); + if (ofs < 0) + goto error; + } + return 0; + + error: + dev_err(bus->dev, "Too many BDL entries: buffer=%d, period=%d\n", + azx_dev->bufsize, period_bytes); + return -EINVAL; +} + +int snd_i2s_stream_set_params(struct i2s_stream *azx_dev, + unsigned int format_val) +{ + unsigned int bufsize, period_bytes; + struct snd_pcm_substream *substream = azx_dev->substream; + struct snd_pcm_runtime *runtime; + int err; + + if (!substream) + return -EINVAL; + + runtime = substream->runtime; + bufsize = snd_pcm_lib_buffer_bytes(substream); + period_bytes = snd_pcm_lib_period_bytes(substream); + if (bufsize != azx_dev->bufsize || + period_bytes != azx_dev->period_bytes || + format_val != azx_dev->format_val || + runtime->no_period_wakeup != azx_dev->no_period_wakeup) { + + azx_dev->bufsize = bufsize; + azx_dev->period_bytes = period_bytes; + azx_dev->format_val = format_val; + azx_dev->no_period_wakeup = runtime->no_period_wakeup; + err = snd_i2s_stream_setup_periods(azx_dev); + if (err < 0) + return err; + } + + return 0; +} + +int snd_i2s_stream_setup(struct i2s_stream *azx_dev, int pcie, u32 paddr) +{ + struct snd_pcm_runtime *runtime; + + if (azx_dev->substream) + runtime = azx_dev->substream->runtime; + else + runtime = NULL; + + i2s_write_reg(azx_dev->sd_addr, DMA_CHAL_CONFG0, 0x8180); + i2s_write_reg(azx_dev->sd_addr, DMA_MASK_INT, 0x80000003); + + if (azx_dev->direction == SNDRV_PCM_STREAM_PLAYBACK) { + i2s_write_reg(azx_dev->sd_addr, DMA_BDLPL(0), (u32)azx_dev->bdl.addr); + i2s_write_reg(azx_dev->sd_addr, DMA_BDLPU(0), upper_32_bits(azx_dev->bdl.addr)); + if (pcie) + i2s_write_reg(azx_dev->sd_addr, DMA_CHALX_DEV_ADDR(0), 0x1c8); + else + i2s_write_reg(azx_dev->sd_addr, DMA_CHALX_DEV_ADDR(0), paddr + 0x1c8); + i2s_write_reg(azx_dev->sd_addr, DMA_CHALX_CBL(0), azx_dev->bufsize); + i2s_write_reg(azx_dev->sd_addr, DMA_CHALX_LVI(0), azx_dev->frags - 1); + i2s_write_reg(azx_dev->sd_addr, DMA_CHALX_DSIZE(0), 0x2);//0x2 + i2s_write_reg(azx_dev->sd_addr, DMA_CHALX_DLENTH(0), 0x0);//0x0 + } else { + i2s_write_reg(azx_dev->sd_addr, DMA_BDLPL(1), (u32)azx_dev->bdl.addr); + i2s_write_reg(azx_dev->sd_addr, DMA_BDLPU(1), upper_32_bits(azx_dev->bdl.addr)); + if (pcie) + i2s_write_reg(azx_dev->sd_addr, DMA_CHALX_DEV_ADDR(1), 0x1c0); + else + i2s_write_reg(azx_dev->sd_addr, DMA_CHALX_DEV_ADDR(1), paddr + 0x1c0); + i2s_write_reg(azx_dev->sd_addr, DMA_CHALX_CBL(1), azx_dev->bufsize); + i2s_write_reg(azx_dev->sd_addr, DMA_CHALX_LVI(1), azx_dev->frags - 1); + i2s_write_reg(azx_dev->sd_addr, DMA_CHALX_DSIZE(1), 0x8);//0x8 + i2s_write_reg(azx_dev->sd_addr, DMA_CHALX_DLENTH(1), 0x0); + } + + if (runtime && runtime->period_size > 64) + azx_dev->delay_negative_threshold = + -frames_to_bytes(runtime, 64); + else + azx_dev->delay_negative_threshold = 0; + + return 0; +} + +static int phytium_pcm_prepare(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct i2s_phytium *dev = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); + + struct azx *chip = &dev->chip; + struct azx_dev *azx_dev = get_azx_dev(substream); + struct i2sc_bus *bus = azx_bus(chip); + struct i2s_stream *hstr_p; + int err; + + dev->substream = substream; + azx_dev->core.substream = substream; + azx_dev->core.sd_addr = dev->regs_db; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + azx_dev->core.bdl.area = bus->bdl0.area; + azx_dev->core.bdl.addr = bus->bdl0.addr; + } else { + azx_dev->core.bdl.area = bus->bdl1.area; + azx_dev->core.bdl.addr = bus->bdl1.addr; + } + + if (!substream) + return -EINVAL; + + hstr_p = azx_stream(azx_dev); + hstr_p->direction = substream->stream; + + err = snd_i2s_stream_set_params(azx_stream(azx_dev), 0); + if (err < 0) + goto unlock; + + snd_i2s_stream_setup(azx_stream(azx_dev), dev->pcie, dev->paddr); + + unlock: + if (!err) + azx_stream(azx_dev)->prepared = 1; + + return err; +} + +void snd_i2s_stream_clear(struct i2s_stream *azx_dev) +{ + if (azx_dev->direction == SNDRV_PCM_STREAM_PLAYBACK) + i2s_write_reg(azx_dev->sd_addr, DMA_CHALX_CTL(0), 0x0); + else + i2s_write_reg(azx_dev->sd_addr, DMA_CHALX_CTL(1), 0x0); + + azx_dev->running = false; +} + +void snd_i2s_stream_stop(struct i2s_stream *azx_dev) +{ + snd_i2s_stream_clear(azx_dev); +} + +void snd_i2s_stream_start(struct i2s_stream *azx_dev, bool fresh_start) +{ + if (azx_dev->direction == SNDRV_PCM_STREAM_PLAYBACK) + i2s_write_reg(azx_dev->sd_addr, DMA_CHALX_CTL(0), 0x1); + else + i2s_write_reg(azx_dev->sd_addr, DMA_CHALX_CTL(1), 0x5); + + azx_dev->running = true; +} + +static int phytium_pcm_trigger(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int cmd) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct i2s_phytium *dev = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); + + struct azx *chip = &dev->chip; + struct i2sc_bus *bus = azx_bus(chip); + struct azx_dev *azx_dev = get_azx_dev(substream); + struct snd_pcm_substream *s; + struct i2s_stream *hstr; + bool start; + int sbits = 0; + + hstr = azx_stream(azx_dev); + hstr->direction = substream->stream; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + start = true; + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_STOP: + start = false; + break; + default: + return -EINVAL; + } + + snd_pcm_group_for_each_entry(s, substream) { + if (s->pcm->card != substream->pcm->card) + continue; + azx_dev = get_azx_dev(s); + sbits |= 1 << azx_dev->core.index; + snd_pcm_trigger_done(s, substream); + } + + spin_lock(&bus->reg_lock); + + snd_pcm_group_for_each_entry(s, substream) { + if (s->pcm->card != substream->pcm->card) + continue; + azx_dev = get_azx_dev(s); + if (start) + snd_i2s_stream_start(azx_stream(azx_dev), true); + else + snd_i2s_stream_stop(azx_stream(azx_dev)); + } + + i2s_write_reg(dev->regs_db, DMA_CTL, 0x1); + spin_unlock(&bus->reg_lock); + + return 0; +} + +void snd_i2s_stream_cleanup(struct i2s_stream *azx_dev) +{ + int cnt = 10; + u32 mask; + + if (azx_dev->sd_addr) { + if (azx_dev->direction == SNDRV_PCM_STREAM_PLAYBACK) { + mask = i2s_read_reg(azx_dev->sd_addr, DMA_MASK_INT); + mask &= ~BIT(0); + i2s_write_reg(azx_dev->sd_addr, DMA_MASK_INT, mask); + i2s_write_reg(azx_dev->sd_addr, DMA_CHALX_CTL(0), 0); + while (cnt--) { + if (i2s_read_reg(azx_dev->sd_addr, DMA_CHALX_CTL(0)) == 0) + break; + } + i2s_write_reg(azx_dev->sd_addr, DMA_CHALX_CTL(0), 2); + i2s_write_reg(azx_dev->sd_addr, DMA_CHALX_CTL(0), 0); + i2s_write_reg(azx_dev->sd_addr, DMA_BDLPL(0), 0); + i2s_write_reg(azx_dev->sd_addr, DMA_BDLPU(0), 0); + } else { + mask = i2s_read_reg(azx_dev->sd_addr, DMA_MASK_INT); + mask &= ~BIT(1); + i2s_write_reg(azx_dev->sd_addr, DMA_MASK_INT, mask); + i2s_write_reg(azx_dev->sd_addr, DMA_CHALX_CTL(1), 0); + while (cnt--) { + if (i2s_read_reg(azx_dev->sd_addr, DMA_CHALX_CTL(1)) == 0) + break; + } + i2s_write_reg(azx_dev->sd_addr, DMA_CHALX_CTL(1), 2); + i2s_write_reg(azx_dev->sd_addr, DMA_CHALX_CTL(1), 0); + i2s_write_reg(azx_dev->sd_addr, DMA_BDLPL(1), 0); + i2s_write_reg(azx_dev->sd_addr, DMA_BDLPU(1), 0); + } + } +} + +static int phytium_pcm_hw_free(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct i2s_phytium *dev = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); + + struct azx *chip = &dev->chip; + struct i2s_stream *hstr_p; + struct azx_dev *azx_dev = get_azx_dev(substream); + int err; + + hstr_p = azx_stream(azx_dev); + hstr_p->direction = substream->stream; + snd_i2s_stream_cleanup(azx_stream(azx_dev)); + + err = chip->ops->substream_free_pages(chip, substream); + azx_stream(azx_dev)->prepared = 0; + + return err; +} + +static snd_pcm_uframes_t phytium_pcm_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct i2s_phytium *dev = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); + + int stream = substream->stream; + + u32 pos = i2s_read_reg(dev->regs_db, DMA_LPIB(stream)); + + return bytes_to_frames(substream->runtime, pos); +} + +static const struct snd_soc_component_driver phytium_i2s_component = { + .name = "phytium-i2s", + .pcm_construct = phytium_pcm_new, + .suspend = phytium_i2s_suspend, + .resume = phytium_i2s_resume, + + .open = phytium_pcm_open, + .close = phytium_pcm_close, + .hw_params = phytium_pcm_hw_params, + .prepare = phytium_pcm_prepare, + .hw_free = phytium_pcm_hw_free, + .trigger = phytium_pcm_trigger, + .pointer = phytium_pcm_pointer, +}; + +/* Maximum bit resolution of a channel - not uniformly spaced */ +static const u32 fifo_width[COMP_MAX_WORDSIZE] = { + 12, 16, 20, 24, 32, 0, 0, 0 +}; + +/* Width of (DMA) bus */ +static const u32 bus_widths[COMP_MAX_DATA_WIDTH] = { + DMA_SLAVE_BUSWIDTH_1_BYTE, + DMA_SLAVE_BUSWIDTH_2_BYTES, + DMA_SLAVE_BUSWIDTH_4_BYTES, + DMA_SLAVE_BUSWIDTH_UNDEFINED +}; + +/* PCM format to support channel resolution */ +static const u32 formats[COMP_MAX_WORDSIZE] = { + SNDRV_PCM_FMTBIT_S16_LE, + SNDRV_PCM_FMTBIT_S16_LE, + SNDRV_PCM_FMTBIT_S24_LE, + SNDRV_PCM_FMTBIT_S24_LE, + SNDRV_PCM_FMTBIT_S32_LE, + 0, + 0, + 0 +}; + +static int phytium_configure_dai(struct i2s_phytium *dev) +{ + u32 comp1 = i2s_read_reg(dev->regs, dev->i2s_reg_comp1); + u32 comp2 = i2s_read_reg(dev->regs, dev->i2s_reg_comp2); + u32 fifo_depth = 1 << (1 + COMP1_FIFO_DEPTH_GLOBAL(comp1)); + u32 idx; + + if (COMP1_TX_ENABLED(comp1)) { + dev_dbg(dev->dev, " phytium: play supported\n"); + idx = COMP1_TX_WORDSIZE_0(comp1); + if (WARN_ON(idx >= ARRAY_SIZE(formats))) + return -EINVAL; + } + + if (COMP1_RX_ENABLED(comp1)) { + dev_dbg(dev->dev, "phytium: record supported\n"); + idx = COMP2_RX_WORDSIZE_0(comp2); + if (WARN_ON(idx >= ARRAY_SIZE(formats))) + return -EINVAL; + if (dev->quirks & PHYTIUM_I2S_QUIRK_16BIT_IDX_OVERRIDE) + idx = 1; + } + + if (COMP1_MODE_EN(comp1)) { + dev_dbg(dev->dev, "phytium: i2s master mode supported\n"); + dev->capability |= PHYTIUM_I2S_MASTER; + } else { + dev_dbg(dev->dev, "phytium: i2s slave mode supported\n"); + dev->capability |= PHYTIUM_I2S_SLAVE; + } + + dev->fifo_th = fifo_depth / 2; + return 0; +} + +static int phytium_configure_dai_by_dt(struct i2s_phytium *dev) +{ + u32 comp1 = i2s_read_reg(dev->regs, I2S_COMP_PARAM_1); + u32 comp2 = i2s_read_reg(dev->regs, I2S_COMP_PARAM_2); + u32 idx = COMP1_APB_DATA_WIDTH(comp1); + u32 idx2; + int ret; + + if (WARN_ON(idx >= ARRAY_SIZE(bus_widths))) + return -EINVAL; + + ret = phytium_configure_dai(dev); + if (ret < 0) + return ret; + + if (COMP1_TX_ENABLED(comp1)) { + idx2 = COMP1_TX_WORDSIZE_0(comp1); + dev->capability |= PHYTIUM_I2S_PLAY; + } + if (COMP1_RX_ENABLED(comp1)) { + idx2 = COMP2_RX_WORDSIZE_0(comp2); + dev->capability |= PHYTIUM_I2S_RECORD; + } + + return 0; +} + +static int phytium_dma_alloc_pages(struct i2sc_bus *bus, int type, size_t size, + struct snd_dma_buffer *buf) +{ + int err; + + err = snd_dma_alloc_pages(type, bus->dev, size, buf); + if (err < 0) + return err; + + return 0; +} + +int snd_i2s_bus_alloc_stream_pages(struct i2sc_bus *bus) +{ + struct i2s_stream *s; + int num_streams = 0; + int err; + + list_for_each_entry(s, &bus->stream_list, list) { + + /* allocate memory for the BDL for each stream */ + err = bus->io_ops->dma_alloc_pages(bus, SNDRV_DMA_TYPE_DEV, + BDL_SIZE, &s->bdl); + if (num_streams == 0) { + bus->bdl0.addr = s->bdl.addr; + bus->bdl0.area = s->bdl.area; + } else { + bus->bdl1.addr = s->bdl.addr; + bus->bdl1.area = s->bdl.area; + } + num_streams++; + if (err < 0) + return -ENOMEM; + } + + if (WARN_ON(!num_streams)) + return -EINVAL; + + return 0; +} + +static int stream_direction(struct azx *chip, unsigned char index) +{ + if (index >= chip->playback_index_offset && + index < chip->playback_index_offset + chip->playback_streams) + return SNDRV_PCM_STREAM_PLAYBACK; + return SNDRV_PCM_STREAM_CAPTURE; + +} + +void snd_i2s_stream_init(struct i2sc_bus *bus, struct i2s_stream *azx_dev, + int idx, int direction, int tag) +{ + azx_dev->bus = bus; + azx_dev->sd_addr = bus->remap_addr; + + if (idx == 0) + azx_dev->sd_int_sta_mask = 1 << idx; + else + azx_dev->sd_int_sta_mask = 1 << 8; + + azx_dev->index = idx; + azx_dev->direction = direction; + azx_dev->stream_tag = tag; + + list_add_tail(&azx_dev->list, &bus->stream_list); + +} + +int azx_i2s_init_streams(struct azx *chip) +{ + int i; + + for (i = 0; i < chip->num_streams; i++) { + struct azx_dev *azx_dev = kzalloc(sizeof(*azx_dev), GFP_KERNEL); + int dir, tag; + + if (!azx_dev) + return -ENOMEM; + + dir = stream_direction(chip, i); + + tag = i + 1; + + snd_i2s_stream_init(azx_bus(chip), azx_stream(azx_dev), + i, dir, tag); + } + + return 0; +} + +static int azx_first_init(struct azx *chip) +{ + struct i2s_phytium *i2s = container_of(chip, struct i2s_phytium, chip); + struct platform_device *pdev = to_platform_device(i2s->dev); + struct device *i2sdev = i2s->dev; + struct i2sc_bus *bus = azx_bus(chip); + struct resource *res; + int err; + unsigned int dma_bits = 64; + + chip->region_requested = 1; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + bus->addr = res->start; + bus->remap_addr = i2s->regs_db; + bus->dev = i2s->pdev; + + if (bus->remap_addr == NULL) { + dev_err(i2sdev, "ioremap error\n"); + return -ENXIO; + } + + if (azx_acquire_irq(chip, 0) < 0) + return -EBUSY; + + synchronize_irq(bus->irq); + + spin_lock_init(&bus->reg_lock); + + if (!dma_set_mask(i2sdev, DMA_BIT_MASK(dma_bits))) { + err = dma_set_coherent_mask(i2sdev, DMA_BIT_MASK(dma_bits)); + } else { + err = dma_set_mask(i2sdev, DMA_BIT_MASK(32)); + err = dma_set_coherent_mask(i2sdev, DMA_BIT_MASK(32)); + } + + chip->playback_streams = NUM_PLAYBACK; + chip->capture_streams = NUM_CAPTURE; + + chip->playback_index_offset = 0; + chip->capture_index_offset = chip->playback_streams; + chip->num_streams = chip->playback_streams + chip->capture_streams; + + err = azx_i2s_init_streams(chip); + if (err < 0) + return err; + + err = azx_alloc_stream_pages(chip); + if (err < 0) + return err; + + return 0; +} + +static int azx_probe_continue(struct azx *chip) +{ + struct i2s_phytium *i2s = container_of(chip, struct i2s_phytium, chip); + int err; + + i2s->probe_continued = 1; + + err = azx_first_init(chip); + if (err < 0) + goto out_free; + + chip->running = 1; + +out_free: + return err; +} + +static void azx_probe_work(struct work_struct *work) +{ + struct i2s_phytium *i2s = container_of(work, struct i2s_phytium, probe_work); + + azx_probe_continue(&i2s->chip); +} + +int azx_i2s_bus_init(struct azx *chip, + const struct i2s_io_ops *io_ops) +{ + struct i2s_bus *bus = &chip->bus; + + bus->core.io_ops = io_ops; + + INIT_LIST_HEAD(&bus->core.stream_list); + bus->card = chip->card; + mutex_init(&bus->prepare_mutex); + bus->pci = chip->pci; + + bus->core.bdl_pos_adj = chip->bdl_pos_adj; + return 0; +} + +static int i2s_phytium_create(struct platform_device *pdev, + int dev, struct azx **rchip, struct i2s_phytium *i2s) +{ + struct azx *chip; + int err; + + *rchip = NULL; + + if (!i2s) + return -ENOMEM; + chip = &i2s->chip; + + mutex_init(&chip->open_mutex); + + chip->ops = &axi_i2s_ops; + chip->dev_index = dev; + + INIT_LIST_HEAD(&chip->pcm_list); + init_completion(&i2s->probe_wait); + + chip->bdl_pos_adj = 32; + err = azx_i2s_bus_init(chip, &axi_i2s_io_ops); + if (err < 0) { + kfree(i2s); + return err; + } + + INIT_WORK(&i2s->probe_work, azx_probe_work); + *rchip = chip; + return 0; +} + +static int substream_alloc_pages(struct azx *chip, + struct snd_pcm_substream *substream, + size_t size) +{ + int ret; + + ret = snd_pcm_lib_malloc_pages(substream, size); + if (ret < 0) + return ret; + + return 0; +} + +static void phytium_dma_free_pages(struct i2sc_bus *bus, + struct snd_dma_buffer *buf) +{ + snd_dma_free_pages(buf); +} + +static const struct i2s_io_ops axi_i2s_io_ops = { + .dma_alloc_pages = phytium_dma_alloc_pages, + .dma_free_pages = phytium_dma_free_pages, +}; + +static const struct i2s_controller_ops axi_i2s_ops = { + .substream_alloc_pages = substream_alloc_pages, + .substream_free_pages = substream_free_pages, +}; + + +static int phytium_i2s_probe(struct platform_device *pdev) +{ + struct i2s_phytium *i2s; + struct azx *chip; + struct resource *res; + struct pdata_px210_mfd *pdata; + struct snd_soc_dai_driver *dai_drv; + struct clk *clk; + int err, ret; + int card_num = 1; + bool schedule_probe; + struct fwnode_handle *np; + + i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL); + if (!i2s) + return -ENOMEM; + + dai_drv = devm_kzalloc(&pdev->dev, sizeof(*dai_drv), GFP_KERNEL); + if (!dai_drv) + return -ENOMEM; + memcpy(dai_drv, &phytium_i2s_dai, sizeof(phytium_i2s_dai)); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + i2s->paddr = res->start; + i2s->regs = devm_ioremap_resource(&pdev->dev, res); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + i2s->regs_db = devm_ioremap_resource(&pdev->dev, res); + + if (IS_ERR(i2s->regs)) + return PTR_ERR(i2s->regs); + + i2s->irq_id = platform_get_irq(pdev, 0); + + if (i2s->irq_id < 0) + return i2s->irq_id; + + i2s->i2s_reg_comp1 = I2S_COMP_PARAM_1; + i2s->i2s_reg_comp2 = I2S_COMP_PARAM_2; + + ret = phytium_configure_dai_by_dt(i2s); + if (ret < 0) + return ret; + + err = i2s_phytium_create(pdev, card_num, &chip, i2s); + if (err < 0) + return err; + i2s = container_of(chip, struct i2s_phytium, chip); + schedule_probe = !chip->disabled; + + dev_set_drvdata(&pdev->dev, i2s); + + pdata = dev_get_platdata(&pdev->dev); + i2s->dev = &pdev->dev; + if (pdata) { + dai_drv->name = pdata->name; + i2s->pdev = pdata->dev; + i2s->clk_base = pdata->clk_base; + i2s->pcie = 1; + } else if (pdev->dev.of_node) { + device_property_read_string(&pdev->dev, "dai-name", &dai_drv->name); + i2s->pdev = &pdev->dev; + clk = devm_clk_get(&pdev->dev, NULL); + i2s->clk_base = clk_get_rate(clk); + } else if (has_acpi_companion(&pdev->dev)) { + np = dev_fwnode(&(pdev->dev)); + ret = fwnode_property_read_string(np, "dai-name", &dai_drv->name); + if (ret < 0) { + dev_err(&pdev->dev, "missing dai-name property from acpi\n"); + goto failed_get_dai_name; + } + i2s->pdev = &pdev->dev; + ret = fwnode_property_read_u32(np, "i2s_clk", &i2s->clk_base); + if (ret < 0) { + dev_err(&pdev->dev, "missing i2s_clk property from acpi\n"); + goto failed_get_dai_name; + } + } + ret = devm_snd_soc_register_component(&pdev->dev, &phytium_i2s_component, + dai_drv, 1); + if (ret != 0) + dev_err(&pdev->dev, "not able to register dai\n"); + + if (schedule_probe) + schedule_work(&i2s->probe_work); + + if (chip->disabled) + complete_all(&i2s->probe_wait); + + return 0; +failed_get_dai_name: + return ret; +} + +static int phytium_i2s_remove(struct platform_device *pdev) +{ + pm_runtime_disable(&pdev->dev); + return 0; +} + +static const struct of_device_id phytium_i2s_of_match[] = { + { .compatible = "phytium,i2s", }, + {}, +}; +MODULE_DEVICE_TABLE(of, phytium_i2s_of_match); + +#ifdef CONFIG_ACPI +static const struct acpi_device_id phytium_i2s_acpi_match[] = { + { "PHYT0016", 0}, + { } +}; +MODULE_DEVICE_TABLE(acpi, phytium_i2s_acpi_match); +#else +#define phytium_i2s_acpi_match NULL +#endif + +static struct platform_driver phytium_i2s_driver = { + .probe = phytium_i2s_probe, + .remove = phytium_i2s_remove, + .driver = { + .name = "phytium-i2s", + .of_match_table = of_match_ptr(phytium_i2s_of_match), + .acpi_match_table = phytium_i2s_acpi_match, + }, +}; + +module_platform_driver(phytium_i2s_driver); + +MODULE_DESCRIPTION("Phytium I2S Driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Zhang Yiqun "); diff --git a/target/linux/phytium/files-5.10/sound/soc/phytium/pmdk_dp.c b/target/linux/phytium/files-5.10/sound/soc/phytium/pmdk_dp.c new file mode 100755 index 000000000..ec045e9a4 --- /dev/null +++ b/target/linux/phytium/files-5.10/sound/soc/phytium/pmdk_dp.c @@ -0,0 +1,235 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include + +#define DAI_CNT(pmdk_dai_) sizeof(pmdk_dai_)/sizeof(struct snd_soc_dai_link) + +struct pmdk_dp_private { + struct snd_soc_jack jack0; + struct snd_soc_jack jack1; + struct snd_soc_jack jack2; +}; + +/* PMDK widgets */ +static const struct snd_soc_dapm_widget pmdk_dp_dapm_widgets[] = { + SND_SOC_DAPM_LINE("DP", NULL), +}; + +/* PMDK control */ +static const struct snd_kcontrol_new pmdk_controls[] = { + SOC_DAPM_PIN_SWITCH("DP"), +}; + +/* PMDK connections */ +static const struct snd_soc_dapm_route pmdk_dp_audio_map[] = { + {"DP", NULL, "TX"}, +}; + +static struct snd_soc_jack_pin dp0_pins[] = { + { + .pin = "HDMI/DP,pcm=0", + .mask = SND_JACK_LINEOUT, + }, +}; + +static struct snd_soc_jack_pin dp1_pins[] = { + { + .pin = "HDMI/DP,pcm=1", + .mask = SND_JACK_LINEOUT, + }, +}; + +static struct snd_soc_jack_pin dp2_pins[] = { + { + .pin = "HDMI/DP,pcm=2", + .mask = SND_JACK_LINEOUT, + }, +}; + +#define SMDK_DAI_FMT (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | \ + SND_SOC_DAIFMT_CBS_CFS) + +static int pmdk_dp0_init(struct snd_soc_pcm_runtime *runtime) +{ + struct snd_soc_card *card = runtime->card; + struct pmdk_dp_private *priv = snd_soc_card_get_drvdata(card); + struct snd_soc_component *component = asoc_rtd_to_codec(runtime, 0)->component; + int ret; + + ret = snd_soc_card_jack_new(card, "HDMI/DP,pcm=0", + SND_JACK_LINEOUT, + &priv->jack0, dp0_pins, + ARRAY_SIZE(dp0_pins)); + if (ret) { + dev_err(card->dev, "Jack creation failed %d\n", ret); + return ret; + } + snd_soc_component_set_jack(component, &priv->jack0, NULL); + return ret; +} + +static int pmdk_dp1_init(struct snd_soc_pcm_runtime *runtime) +{ + struct snd_soc_card *card = runtime->card; + struct pmdk_dp_private *priv = snd_soc_card_get_drvdata(card); + struct snd_soc_component *component = asoc_rtd_to_codec(runtime, 0)->component; + int ret; + + ret = snd_soc_card_jack_new(card, "HDMI/DP,pcm=1", + SND_JACK_LINEOUT, + &priv->jack1, dp1_pins, + ARRAY_SIZE(dp1_pins)); + + if (ret) { + dev_err(card->dev, "Jack creation failed %d\n", ret); + return ret; + } + snd_soc_component_set_jack(component, &priv->jack1, NULL); + return ret; +} + +static int pmdk_dp2_init(struct snd_soc_pcm_runtime *runtime) +{ + struct snd_soc_card *card = runtime->card; + struct pmdk_dp_private *priv = snd_soc_card_get_drvdata(card); + struct snd_soc_component *component = asoc_rtd_to_codec(runtime, 0)->component; + int ret; + + ret = snd_soc_card_jack_new(card, "HDMI/DP,pcm=2", + SND_JACK_LINEOUT, + &priv->jack2, dp2_pins, + ARRAY_SIZE(dp2_pins)); + if (ret) { + dev_err(card->dev, "Jack creation failed %d\n", ret); + return ret; + } + snd_soc_component_set_jack(component, &priv->jack2, NULL); + return ret; +} + +SND_SOC_DAILINK_DEFS(pmdk_dp0_dai, + DAILINK_COMP_ARRAY(COMP_CPU("phytium-i2s-dp0")), + DAILINK_COMP_ARRAY(COMP_CODEC("hdmi-audio-codec.1346918656", "i2s-hifi")), + DAILINK_COMP_ARRAY(COMP_PLATFORM("snd-soc-dummy"))); + +SND_SOC_DAILINK_DEFS(pmdk_dp1_dai, + DAILINK_COMP_ARRAY(COMP_CPU("phytium-i2s-dp1")), + DAILINK_COMP_ARRAY(COMP_CODEC("hdmi-audio-codec.1346918657", "i2s-hifi")), + DAILINK_COMP_ARRAY(COMP_PLATFORM("snd-soc-dummy"))); + +SND_SOC_DAILINK_DEFS(pmdk_dp2_dai, + DAILINK_COMP_ARRAY(COMP_CPU("phytium-i2s-dp2")), + DAILINK_COMP_ARRAY(COMP_CODEC("hdmi-audio-codec.1346918658", "i2s-hifi")), + DAILINK_COMP_ARRAY(COMP_PLATFORM("snd-soc-dummy"))); + +static struct snd_soc_dai_link pmdk_dai_local[] = { +{ + .name = "Phytium dp0-audio", + .stream_name = "Playback", + .dai_fmt = SMDK_DAI_FMT, + .init = pmdk_dp0_init, + SND_SOC_DAILINK_REG(pmdk_dp0_dai), +},{ + .name = "Phytium dp1-audio", + .stream_name = "Playback", + .dai_fmt = SMDK_DAI_FMT, + .init = pmdk_dp1_init, + SND_SOC_DAILINK_REG(pmdk_dp1_dai), +}, +{ + .name = "Phytium dp2-audio", + .stream_name = "Playback", + .dai_fmt = SMDK_DAI_FMT, + .init = pmdk_dp2_init, + SND_SOC_DAILINK_REG(pmdk_dp2_dai), +}, +}; + +static struct snd_soc_card pmdk = { + .name = "PMDK-I2S", + .owner = THIS_MODULE, + + .dapm_widgets = pmdk_dp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(pmdk_dp_dapm_widgets), + .controls = pmdk_controls, + .num_controls = ARRAY_SIZE(pmdk_controls), + .dapm_routes = pmdk_dp_audio_map, + .num_dapm_routes = ARRAY_SIZE(pmdk_dp_audio_map), +}; + +static int pmdk_sound_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &pmdk; + struct pmdk_dp_private *priv; + struct snd_soc_dai_link *pmdk_dai; + int num_dp = 2; + char dp_mask = 0x7; + int i,j = 0; + card->dev = &pdev->dev; + + device_property_read_u32(&pdev->dev, "num-dp", &num_dp); + device_property_read_u8(&pdev->dev, "dp-mask", &dp_mask); + pmdk_dai = devm_kzalloc(&pdev->dev, num_dp * sizeof(*pmdk_dai), GFP_KERNEL); + if (!pmdk_dai) + return -ENOMEM; + + if (!num_dp || num_dp > DAI_CNT(pmdk_dai_local)) + return -EINVAL; + + for(i = 0; i < num_dp; i++) { + for (; j < DAI_CNT(pmdk_dai_local); j++) { + if (BIT(j) & dp_mask) { + pmdk_dai[i] = pmdk_dai_local[j]; + j++; + break; + } + } + } + + card->dai_link = pmdk_dai; + card->num_links = num_dp; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + snd_soc_card_set_drvdata(card, priv); + + return devm_snd_soc_register_card(&pdev->dev, card); +} + +static const struct of_device_id pmdk_sound_of_match[] = { + { .compatible = "phytium,pmdk-dp",}, + { } +}; +MODULE_DEVICE_TABLE(of, pmdk_sound_of_match); + +static const struct acpi_device_id pmdk_sound_acpi_match[] = { + { "PHYT8006", 0}, + { } +}; +MODULE_DEVICE_TABLE(acpi, pmdk_sound_acpi_match); + +static struct platform_driver pmdk_sound_driver = { + .probe = pmdk_sound_probe, + .driver = { + .name = "pmdk_dp", + .acpi_match_table = pmdk_sound_acpi_match, + .of_match_table = pmdk_sound_of_match, +#ifdef CONFIG_PM + .pm = &snd_soc_pm_ops, +#endif + }, +}; + +module_platform_driver(pmdk_sound_driver); + +MODULE_AUTHOR("Zhang Yiqun"); +MODULE_DESCRIPTION("ALSA SoC PMDK DP"); +MODULE_LICENSE("GPL"); diff --git a/target/linux/phytium/files-5.10/sound/soc/phytium/pmdk_es8336.c b/target/linux/phytium/files-5.10/sound/soc/phytium/pmdk_es8336.c new file mode 100644 index 000000000..f27404373 --- /dev/null +++ b/target/linux/phytium/files-5.10/sound/soc/phytium/pmdk_es8336.c @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include + +/* PMDK widgets */ +static const struct snd_soc_dapm_widget pmdk_es8336_dapm_widgets[] = { + SND_SOC_DAPM_HP("HP", NULL), + SND_SOC_DAPM_MIC("Int Mic", NULL), + SND_SOC_DAPM_MIC("Mic In", NULL), +}; + +/* PMDK control */ +static const struct snd_kcontrol_new pmdk_controls[] = { + SOC_DAPM_PIN_SWITCH("HP"), + SOC_DAPM_PIN_SWITCH("Int Mic"), + SOC_DAPM_PIN_SWITCH("Mic In"), +}; + +/* PMDK connections */ +static const struct snd_soc_dapm_route pmdk_es8336_audio_map[] = { + {"DMIC", NULL, "Int Mic"}, + {"MIC1", NULL, "Mic In"}, + {"MIC2", NULL, "Mic In"}, + + {"HP", NULL, "HPOL"}, + {"HP", NULL, "HPOR"}, +}; + +#define PMDK_DAI_FMT (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | \ + SND_SOC_DAIFMT_CBS_CFS) + +SND_SOC_DAILINK_DEFS(pmdk_es8366, + DAILINK_COMP_ARRAY(COMP_CPU("phytium-i2s-lsd")), + DAILINK_COMP_ARRAY(COMP_CODEC("i2c-ESSX8336:00", "es8336-hifi")), + DAILINK_COMP_ARRAY(COMP_PLATFORM("snd-soc-dummy"))); + +static struct snd_soc_dai_link pmdk_dai[] = { + { + .name = "ES8336 HIFI", + .stream_name = "ES8336 HIFI", + .dai_fmt = PMDK_DAI_FMT, + SND_SOC_DAILINK_REG(pmdk_es8366), + }, +}; + +static struct snd_soc_card pmdk = { + .name = "PMDK-I2S", + .owner = THIS_MODULE, + .dai_link = pmdk_dai, + .num_links = ARRAY_SIZE(pmdk_dai), + + .dapm_widgets = pmdk_es8336_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(pmdk_es8336_dapm_widgets), + .controls = pmdk_controls, + .num_controls = ARRAY_SIZE(pmdk_controls), + .dapm_routes = pmdk_es8336_audio_map, + .num_dapm_routes = ARRAY_SIZE(pmdk_es8336_audio_map), +}; + +static int pmdk_sound_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &pmdk; + struct device *dev = &pdev->dev; + + card->dev = dev; + + return devm_snd_soc_register_card(&pdev->dev, card); +} + +static const struct acpi_device_id pmdk_sound_acpi_match[] = { + { "PHYT8005", 0}, + { } +}; +MODULE_DEVICE_TABLE(acpi, pmdk_sound_acpi_match); + +static struct platform_driver pmdk_sound_driver = { + .probe = pmdk_sound_probe, + .driver = { + .name = "pmdk_es8336", + .acpi_match_table = pmdk_sound_acpi_match, +#ifdef CONFIG_PM + .pm = &snd_soc_pm_ops, +#endif + }, +}; + +module_platform_driver(pmdk_sound_driver); +MODULE_AUTHOR("Zhang Yiqun "); +MODULE_DESCRIPTION("ALSA SoC PMDK ES8336"); +MODULE_LICENSE("GPL"); diff --git a/target/linux/phytium/files-5.10/sound/soc/phytium/pmdk_es8388.c b/target/linux/phytium/files-5.10/sound/soc/phytium/pmdk_es8388.c new file mode 100644 index 000000000..81b61c132 --- /dev/null +++ b/target/linux/phytium/files-5.10/sound/soc/phytium/pmdk_es8388.c @@ -0,0 +1,169 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2021-2023 Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include + +static struct snd_soc_jack hs_jack; + +/* Headset jack detection DAPM pins */ +static struct snd_soc_jack_pin hs_jack_pins[] = { + { + .pin = "FrontIn", + .mask = SND_JACK_MICROPHONE, + }, + { + .pin = "RearIn", + .mask = SND_JACK_MICROPHONE, + .invert = 1 + }, + { + .pin = "Front", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Rear", + .mask = SND_JACK_HEADPHONE, + .invert = 1 + }, +}; + +/* Headset jack detection gpios */ +static struct snd_soc_jack_gpio hs_jack_gpios[] = { + { + .name = "det", + .report = SND_JACK_HEADSET, + .debounce_time = 200, + .invert = 1, + }, +}; + +/* PMDK widgets */ +static const struct snd_soc_dapm_widget pmdk_es8388_dapm_widgets[] = { + SND_SOC_DAPM_HP("Front", NULL), + SND_SOC_DAPM_HP("Rear", NULL), + + SND_SOC_DAPM_MIC("FrontIn", NULL), + SND_SOC_DAPM_MIC("RearIn", NULL), +}; + +/* PMDK control */ +static const struct snd_kcontrol_new pmdk_controls[] = { + SOC_DAPM_PIN_SWITCH("Front"), + SOC_DAPM_PIN_SWITCH("Rear"), + SOC_DAPM_PIN_SWITCH("FrontIn"), + SOC_DAPM_PIN_SWITCH("RearIn"), +}; + +/* PMDK connections */ +static const struct snd_soc_dapm_route pmdk_es8388_audio_map[] = { + {"LINPUT1", NULL, "FrontIn"}, + {"RINPUT1", NULL, "FrontIn"}, + + {"LINPUT2", NULL, "RearIn"}, + {"RINPUT2", NULL, "RearIn"}, + + {"Front", NULL, "LOUT1"}, + {"Front", NULL, "ROUT1"}, + + {"Rear", NULL, "LOUT2"}, + {"Rear", NULL, "ROUT2"}, +}; + +static int pmdk_es8388_init(struct snd_soc_pcm_runtime *rtd) +{ + int ret; + + /* Jack detection API stuff */ + ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", SND_JACK_HEADSET, + &hs_jack, hs_jack_pins, + ARRAY_SIZE(hs_jack_pins)); + if (ret) + goto err; + + ret = snd_soc_jack_add_gpios(&hs_jack, ARRAY_SIZE(hs_jack_gpios), + hs_jack_gpios); + if (ret) + goto err; + + return 0; + +err: + return ret; +} + +#define PMDK_DAI_FMT (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | \ + SND_SOC_DAIFMT_CBS_CFS) + +SND_SOC_DAILINK_DEFS(pmdk_es8388, + DAILINK_COMP_ARRAY(COMP_CPU("phytium-i2s-lsd")), + DAILINK_COMP_ARRAY(COMP_CODEC("i2c-ESSX8388:00", "es8388-hifi")), + DAILINK_COMP_ARRAY(COMP_PLATFORM("snd-soc-dummy"))); + +static struct snd_soc_dai_link pmdk_dai[] = { + { + .name = "ES8388 HIFI", + .stream_name = "ES8388 HIFI", + .dai_fmt = PMDK_DAI_FMT, + .init = pmdk_es8388_init, + SND_SOC_DAILINK_REG(pmdk_es8388), + }, +}; + +static struct snd_soc_card pmdk = { + .name = "PMDK-I2S", + .owner = THIS_MODULE, + .dai_link = pmdk_dai, + .num_links = ARRAY_SIZE(pmdk_dai), + + .dapm_widgets = pmdk_es8388_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(pmdk_es8388_dapm_widgets), + .controls = pmdk_controls, + .num_controls = ARRAY_SIZE(pmdk_controls), + .dapm_routes = pmdk_es8388_audio_map, + .num_dapm_routes = ARRAY_SIZE(pmdk_es8388_audio_map), +}; + +static int pmdk_sound_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &pmdk; + struct device *dev = &pdev->dev; + int n; + + card->dev = dev; + hs_jack_gpios[0].gpiod_dev = dev; + n = gpiod_count(dev, "det"); + + if (n < 0) + pmdk_dai[0].init = NULL; + + return devm_snd_soc_register_card(&pdev->dev, card); +} + +static const struct acpi_device_id pmdk_sound_acpi_match[] = { + { "PHYT8004", 0}, + { } +}; +MODULE_DEVICE_TABLE(acpi, pmdk_sound_acpi_match); + +static struct platform_driver pmdk_sound_driver = { + .probe = pmdk_sound_probe, + .driver = { + .name = "pmdk_es8388", + .acpi_match_table = pmdk_sound_acpi_match, +#ifdef CONFIG_PM + .pm = &snd_soc_pm_ops, +#endif + }, +}; + +module_platform_driver(pmdk_sound_driver); + +MODULE_AUTHOR("Zhang Yiqun"); +MODULE_DESCRIPTION("ALSA SoC PMDK ES8388"); +MODULE_LICENSE("GPL"); diff --git a/target/linux/phytium/image/Makefile b/target/linux/phytium/image/Makefile new file mode 100644 index 000000000..f0459fc76 --- /dev/null +++ b/target/linux/phytium/image/Makefile @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Copyright (C) 2016-2017 Yousong Zhou +# Copyright (c) 2023 Phytium Technology Co., Ltd. +# Copyright (C) 2023-2024 Ailick <277498654@qq.com> + +include $(TOPDIR)/rules.mk +include $(INCLUDE_DIR)/image.mk + +define Build/Compile + $(CP) $(LINUX_DIR)/COPYING $(KDIR)/COPYING.linux +endef + +include $(SUBTARGET).mk + +$(eval $(call BuildImage)) diff --git a/target/linux/phytium/image/armv8.mk b/target/linux/phytium/image/armv8.mk new file mode 100644 index 000000000..b3db8cc2b --- /dev/null +++ b/target/linux/phytium/image/armv8.mk @@ -0,0 +1,111 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Copyright (C) 2023-2024 Ailick <277498654@qq.com> + +GRUB2_VARIANT = +GRUB_TERMINALS = +GRUB_SERIAL_CONFIG = +GRUB_TERMINAL_CONFIG = +GRUB_CONSOLE_CMDLINE = earlycon + +ifneq ($(CONFIG_GRUB_CONSOLE),) + GRUB_TERMINALS += console +endif + +GRUB_SERIAL:=$(call qstrip,$(CONFIG_GRUB_SERIAL)) + +GRUB_SERIAL_CONFIG := serial --unit=0 --speed=$(CONFIG_GRUB_BAUDRATE) --word=8 --parity=no --stop=1 --rtscts=$(if $(CONFIG_GRUB_FLOWCONTROL),on,off) +GRUB_TERMINALS += serial + +GRUB_TERMINAL_CONFIG := terminal_input $(GRUB_TERMINALS); terminal_output $(GRUB_TERMINALS) + +ROOTPART:=$(call qstrip,$(CONFIG_TARGET_ROOTFS_PARTNAME)) +ROOTPART:=$(if $(ROOTPART),$(ROOTPART),PARTUUID=$(IMG_PART_SIGNATURE)-02) +GPT_ROOTPART:=$(call qstrip,$(CONFIG_TARGET_ROOTFS_PARTNAME)) +GPT_ROOTPART:=$(if $(GPT_ROOTPART),$(GPT_ROOTPART),PARTUUID=$(shell echo $(IMG_PART_DISKGUID) | sed 's/00$$/02/')) + +GRUB_TIMEOUT:=$(call qstrip,$(CONFIG_GRUB_TIMEOUT)) +GRUB_TITLE:=$(call qstrip,$(CONFIG_GRUB_TITLE)) + +BOOTOPTS:=$(call qstrip,$(CONFIG_GRUB_BOOTOPTS)) + +define Build/combined + $(INSTALL_DIR) $@.boot/ + $(CP) $(KDIR)/$(KERNEL_NAME) $@.boot/efi/openwrt/ + -$(CP) $(STAGING_DIR_ROOT)/boot/. $@.boot/boot/ + $(if $(filter $(1),efi), + $(INSTALL_DIR) $@.boot/efi/boot + $(CP) $(STAGING_DIR_IMAGE)/grub2/boot$(if $(CONFIG_aarch64),aa64,arm).efi $@.boot/efi/openwrt/ + $(CP) $(STAGING_DIR_IMAGE)/grub2/boot$(if $(CONFIG_aarch64),aa64,arm).efi $@.boot/efi/boot/ + ) + KERNELPARTTYPE=ef FAT_TYPE="32" PADDING="1" SIGNATURE="$(IMG_PART_SIGNATURE)" \ + $(if $(filter $(1),efi),GUID="$(IMG_PART_DISKGUID)") $(SCRIPT_DIR)/gen_image_generic.sh \ + $@ \ + $(CONFIG_TARGET_KERNEL_PARTSIZE) $@.boot \ + $(CONFIG_TARGET_ROOTFS_PARTSIZE) $(IMAGE_ROOTFS) \ + 256 +endef + +define Build/grub-config + rm -fR $@.boot + $(INSTALL_DIR) $@.boot/efi/openwrt/ + sed \ + -e 's#@SERIAL_CONFIG@#$(strip $(GRUB_SERIAL_CONFIG))#g' \ + -e 's#@TERMINAL_CONFIG@#$(strip $(GRUB_TERMINAL_CONFIG))#g' \ + -e 's#@ROOTPART@#root=$(ROOTPART) rootwait#g' \ + -e 's#@GPT_ROOTPART@#root=$(GPT_ROOTPART) rootwait#g' \ + -e 's#@CMDLINE@#$(BOOTOPTS) $(GRUB_CONSOLE_CMDLINE)#g' \ + -e 's#@TIMEOUT@#$(GRUB_TIMEOUT)#g' \ + -e 's#@TITLE@#$(GRUB_TITLE)#g' \ + -e 's#@KERNEL_NAME@#$(KERNEL_NAME)#g' \ + ./grub-$(1).cfg > $@.boot/efi/openwrt/grub.cfg +endef + +define Build/grub-install + rm -fR $@.grub2 + $(INSTALL_DIR) $@.grub2 +endef + +DEVICE_VARS += GRUB2_VARIANT UBOOT +define Device/efi-default + IMAGE/rootfs.img := append-rootfs | pad-to $(ROOTFS_PARTSIZE) + IMAGE/rootfs.img.gz := append-rootfs | pad-to $(ROOTFS_PARTSIZE) | gzip + IMAGE/combined.img := grub-config efi | combined efi | grub-install efi | append-metadata + IMAGE/combined.img.gz := grub-config efi | combined efi | grub-install efi | gzip | append-metadata + IMAGE/combined.vmdk := grub-config efi | combined efi | grub-install efi | qemu-image vmdk + ifeq ($(CONFIG_TARGET_IMAGES_GZIP),y) + IMAGES-y := rootfs.img.gz + IMAGES-y += combined.img.gz + else + IMAGES-y := rootfs.img + IMAGES-y += combined.img + endif + ifeq ($(CONFIG_VMDK_IMAGES),y) + IMAGES-y += combined.vmdk + endif + KERNEL := kernel-bin + KERNEL_INSTALL := 1 + IMAGES := $$(IMAGES-y) + ARTIFACTS := $$(ARTIFACTS-y) + SUPPORTED_DEVICES := + ifeq ($(CONFIG_arm),y) + KERNEL_NAME = zImage + endif +endef + +define Device/generic + $(call Device/efi-default) + DEVICE_TITLE := Generic EFI Boot + GRUB2_VARIANT := generic + FILESYSTEMS := ext4 squashfs + UBOOT := $(if $(CONFIG_aarch64),qemu_armv8,qemu_armv7) + DEVICE_PACKAGES += kmod-amazon-ena kmod-e1000e kmod-vmxnet3 kmod-rtc-rx8025 \ + kmod-i2c-mux-pca954x kmod-gpio-pca953x partx-utils kmod-wdt-sp805 \ + kmod-mvneta kmod-mvpp2 kmod-fsl-dpaa1-net kmod-fsl-dpaa2-net \ + kmod-fsl-enetc-net kmod-dwmac-imx kmod-fsl-fec kmod-thunderx-net \ + kmod-dwmac-rockchip kmod-dwmac-sun8i kmod-phy-aquantia kmod-phy-broadcom \ + kmod-phy-marvell kmod-phy-marvell-10g kmod-sfp kmod-atlantic \ + kmod-bcmgenet kmod-octeontx2-net +endef +TARGET_DEVICES += generic + diff --git a/target/linux/phytium/image/bin/e2000_miniitx_uboot.bin b/target/linux/phytium/image/bin/e2000_miniitx_uboot.bin new file mode 100644 index 0000000000000000000000000000000000000000..6966cfa7118270733beb22ea6876d36d59d14738 GIT binary patch literal 3145728 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%0DJ=Wu}@>}QT%jasr{cV7QC(Z`;` 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 z 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 z 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 z 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

~mCL61oTHmtjiHD0 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%0AEBN1V5j~ z34teReZt_Eo}Dm zo>}xUs(lW842_=$k8i8mm(XeaGI)f%0?xVBz|W@KwDHaJZ35M80iR6n2Ja+q1&`AD zc){mVdmlLG<_AB6+6Tb9X#5~J=hlHv?IYlvTNIpgOM&-XOZy$XEv-BYzL?tQz!T(o z@Nwh?a5s4oeAP8HPrwtuQ(gvNLF-uoAMt;xeHFazdgV3n^3}?F!1FgK*A8!ey-4}pJ1b;IC`7OVCh;Grj! zN5BWtI8pG}Z&mvkc#?AN0*_M8aq!91J^|kIm>NF`9==<73Osn1@-%pr^2~yF+^pK? zz;C%#c^;> z=Q(=|IA3?$cq=$x_u6;>oUaFMJOu7trg8{_7oS%i0gq7oD0t@!s(lxDWvTKwc$&sZ zfX{tKwajUTQ4S>JM^8uW*dn@4AL{nY?M;F6_NKwdcc{8q@SSL! z9QY{m0{Bp#58zz43?AG<)vbW*m2b=yp^9FVQ$faK0Ut!H?tl0M2!5;B8x~{o4b6JdLBxXr3o=ayR(5^nEX0 zaIWhE_tWv&556hgP6OaR@(}n8`n**KI3G76;QYD8DEJ&2CkB2Qc^7!wZ1sG59DFsk zPl9s}Dexuq`L8ti{WMM%yo){$mIn{h-xnx?bKMg77_ZvzW$?pkoC-J}Pio*39#Y3o zt+RQaaQ-de399P`pF-obf?r1N10VLVsv7|3?G1t_scr~-^kOwm7<>|W1bia@9S3mU z-Y)Pisv8GCfyPOIUqzk*A4%(z1?TO}fyb$C9-Ozg0M6T60$)OPE8x7nRqzYQ zuB)Bcyx$8{w*`D}8pjQOIJp=64st&@*A0N@scsPbZW<>9{wR3|_|A{3{T>D9{Tl;M zQQa3#?NvKy2~ zz`1S|ynMI%JaP#Q5@)9`LErSR0 zs%{0G>sG(}-f9-c_{|3RiZU}tLjjC=K zd=-t;0nWFhC^-K;f-Z2b8wVdl&m$$kwMAiBAs(lWe>*m4xt*>ss1@QH0 zoFe%43G!xK8?NySep&=q+MNigP%n?c)_`@58St&+Fn2S1vE|o zd=YsF{L4>N&K=-fHv%4Btm;O=9elq7?xpWgI`bMWWfvM zdGN6{^?mY1aIRYdwi0A)b@tKbL0{5zdWtl$H2L67r6T%wSVK_D`}hr_`c*R z@P5yzx><0pn*+BFRCV*<18AHA_(<{+c&|?L0i5er!FhXY;N7%cJ>a~(Epwaa&(&17 z6`bpO!MUyvd=S<3gLB;=IRAZwFgPDKI>4`nevQ-pGuwt=f8iE2Isn2@DF~i>gK?mG=3iZeR^G^2!1Jj?^YR{>sG+W#8llX z_`zkBTMhiz#3Dz88EDjpGB~jywR)fA1p%&f6OX4;-zww*&lg z$}<9f6L}2$#hq0?ad57i0FTk@DM|4Duc>iT;6uo>;B(0H;9R!=KAGwk!B3%aO5prD zP6eF*zDW(7>-K>1rO8Xg&6o1G)@=zZR81X{{H+FIM+>s_gtd3Hw!-Fb(LEVd_H*r{PM3= z`w}?UErTz)Le;H+A41<>T?J2%Q1hS%e0Q3MEnUs?gzLJ|FH&_|!Gko87knDIAN=II z)%FI#`FIrq&%dC~tHa=D(>NXAYaaiC7e7~ZyTEyS{INGVr^XFx9 zH~0aDs_O;kx;}7z9>5R&_j}Yh0dU^$A@I}aytD(H=UW7P$OM&h6#N_-CkB2ac^sVo zj!zPt>!!dLw5y!c;5X1b&w}4ao(JdecP)Z*&LwdEe%CU1`ZJYt1-wXJ1?PEQ1LyC3 z)lO@kC!9kIc$yzCgZI$+d@J~XC)IlTz>OLgW&vqvmtQpA8MR1IM25T_#%4V zF9yzayTCh_sQn%Xzn{iQfImr|0w46H`rKI-ocDVUe8fUE&-37}_tf?lzz30+z*kgN z`wBSMt%CcgZVmi38m9;RQ}UM6o9B=BQ}z5$D>&!u1z+>MzTg|vaoZ2>BM*S{JP(5B z-=}#3&g;_wp1noo9|14WI8pGg$h*LY()>(-bN)&2g)3Fv6!>m5P8$3u@*Ma8`uolW zaNgb`c#>|{C2)RyvkcC!Z&tytc}eBm1J2v4ozXl`!n4%&wtz3Baope!kbA)+{Z(B* zIB#zNJoqg=js`!5?jJ+oQSuJ(yU3&9yuC5-eydg8F7W$loH+O;yqPYL`E8ovUr(c`ZgIB#zc_&9p}uAS99 zPd1=&TEO}7dn@>pl)n$0>-xbLQO*JIXK0)-_z2250?y+{!3W-<=1&ZK6phmbeiV5E z{DqIz{cj4K>!!gw@~UnY{97772j0J?=2ZdwO=@2P=elL^R;pV8e~-qgg7f)C4|thw z*DYr^&j+sS2A@mMOSFRDL*sbC`Fzq3{wW;~gWy~@1U^nv?^_Oo*Jzv$@bd?#oTK1z zdS0Rnyyrgpegp6beUCu`d;)n1oWCET0`8{s*igJVXKvpC{{CQ^C*Y+=)q0k|=dV`n z1Lri4pQrXA@UQ53><;h=+Q-pfQR|ZgUv{1vCk?K>rrP&_d&yfaXkMRJX`EK@IJJ+1kEZ#V1W!}@Ja}%R z+O8^if!7m!DBT`gE^J=U+8b)S+~8F=j`m9x+)eA*13o^ga&RY_>!znG z4}u42oG|#`d(?VHz;kqaEP_AgQuTrtH`o1`9*2g(dp=g%n+NZu_C@g48s!7NV0V?j z|B~jq2h;kbzymZ+7W`nkf60TVsC^N<(4Rg}4W90&+&#Z}efax*yx^m$t{*&4b%Wsj zX#5a(Wg}HL0nT+(;L&MxoB{7ge-|bTKFX)s=fD?lt?K5%z4UpX0{9t+s{K_2znGqv zD1mp)ru`1ie_y@=o*kmrrwTrHx?2Alxc_W5eh>IaYOh_|ydPJerP{ZE&!opSZtw{! z)OlSic$mibf={OP@qvHvjaq*{`1mW-`UJp7)A&K~6pbGO|B%KHgC}VG4)Bv`-bTS! zx2gPN;B(32;GN_N@MF(a+nWL(->v3P8a#N5@+|nmh01f_E9m%E03UNT%}?;T*D5c8 z2d+|H1~1e2Rqz0^t1j>b^OdK-OZ0w&EVyr; zy1nGUBPS~_fNOJ<7s1D!q}+W)^ZHMvzX$3C@64Adt z@>cNtSmhny+wtQp@RhXwq2Dyu<=0;d;P(tv^RNnDJ6_eTfmg``3!2C0^QaK`iYL{6 z$$|6pGX-$YtqMN*Ia(iZ>s95xtDD!SosQdSa6V7Vfpgs=c*LjHzXHy6Td!%Z%jb1T za6Vs5foo5z+gBQ#&lj`cp_f$qQvYv1)U?gQ^{?ote^1AtNd3}R|J|hi>#qMce%`Np z!|+EZaMmxqjsJ(Er_P-|Z>D$OVSc~gJ7&tsQ#xnGW==VAu6OTQ6K79uG- zo5pG^AKJA1_6BB{-sPjh{Nye;V7jS^am-_BGnH zM`z8M+dg#Xwc0ecGgx0%zv%13+ui8j_-$PG)_*yN#=aO=|J8zxf9gLCsQ)zDw$y*a zjVtw^-uiFD#@Bz3<{ec3f8P%`(rRkp26m`LYc3l*dLLy}f=jKrRNFpBt$e*{quw_4 z-}NTWo@vGE-nXf1uK0Hzrty`&fg9}`+c$B}%t;OC8?U|_8~<-C+o=A#-kj+(W1Uml zkDu4KyZf9o;!H53M*pMhzxvFXlN!@ut=-bse_&%=o_BQFKJ&YM(AeI-jK7=geG%~b zM)$k$(t4A=v0U}b6KBnscxnUcKYiQ4b%(B*4nMVSQ{+FUZlg`(XI}lcZyn}MoISU1 zVU{V{v`%qf$mbavJk^{wYx+h1x}{ik{RU7x?%K((#YXZd$) zpFXTF>0u*ziyO7pnRC5mqio``fStrc7(Vyo*V|w#-Z~gy`AAR=R zNx_kQ-#FB_tnXMed17bh#5vaFIYWmJ-%YjSevSPdt^YQzW39$^_HFAwA71*}uX8)= zZR_3omVdXM&UC}-KTX?to;G@e2J*T@|DElw*FA1(eJi6uKabhB?I%o`JY|wqU)p}+ ztl5nv-_>hud*g@e^ZNYLc7J!+{HfcWQx$&vb~ge!aq{#z6OZqlGMPFz?StRA6u7GCeED{ojhw=<2K)SSENR6?9+A5&r>GPZM0}CZ%oF%IpAyD zqWXRs)VHi5SJ$V1#>-(R2DDKq<``R>%feqYtw zHi|+v>4w#R)~w6AjuSf9`7yVZb=S$MGC#7;kGUPJyH4RJ)%h`VeBE_QKdH_Xz6VkH zuXC=fyH4dN)tUM)*Qx!KI=j@%_^E^KZE>C3)0ka-KkmlAjlSPE-*)*i=Njv-(>H#7 z`0uYXEjE2t;|4Umad2s@ky@ug^~--cmuj@_Yk{{RXOMVJt;X@?#`_j6aY>9O9LZ62DA_f^*PzRN})>)b}J)i@pSs~)y<%C^=` zeKqQ5_}^b^&cwMB8`}lvLhHKS9yif$yKYe{xmKp@+%8pXn1ARG9~*6J4by{EY|Z{# zXRRr7qf=&|IAv~dcs<4MCQBp7b>3Ij+c(B-EMK#~hG~U{)qlK=e;UhvXpT18tXKcN zW6rdh)B7IT)c3&D>0MJMw|7oIaYlW~cjMe!|8JG9H+*sZcgQ z{m0w*=l|RKH|}%Sn#Vsrf4#BVjUR90pZ|aB|0B-J*ExUmzV)uJdf(;$bp5AIo>{+L z58L}korl!h)>pFe!|PbA|G$xb zynS+;x9aU1{dmqdE;pDtYx0y%ZPKinv59l1*B{hs+f1B1dG?e!bJ`nEz0A?JnKS*g zDfIvPqjbMk|7U39pJ9!EhHI0joIHKfl;h@}8k?fco-$3VKOxpWd*X@H>em|o&e3Mp z_tdP(+N91|lV)gh=1!bjf5z;@Stqv7o-%XR$&H86jd9v%PV8!*I(y1V^QN3Q=~QiQ z{h6Ox^X9hCnKDUhoMfpH=VYwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwA zz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEj zFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r z3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@ z0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VK zfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5 zV8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM z7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b* z1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd z0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwA zz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEj zFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r z3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@ z0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VK zfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5 zV8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM z7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b* z1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd z0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwA zz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEj zFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r z3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@ z0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VK zfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5 zV8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM z7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b* z1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd z0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwA zz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEj zFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r z3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@ z0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VK zfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5 zV8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM z7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b* z1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd z0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwA zz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEj zFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r z3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@ z0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VK zfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5 zV8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM z7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAU>*n_ C1U;_+ literal 0 HcmV?d00001 diff --git a/target/linux/phytium/image/bin/e2000d_demo_uboot.bin b/target/linux/phytium/image/bin/e2000d_demo_uboot.bin new file mode 100644 index 0000000000000000000000000000000000000000..43a8615afdfd711ddf494b663392eff7efc3f72e GIT binary patch literal 3145728 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 z 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%0Dm%+o7go9xjx>V=lJM9&=+i*^hbHV;ZMp&gm|D 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êVyJ z&5wh^Oh283rC8)!t)Gs;GVC+HMg4UcW**}H-?!2L9fSqgp1!36bO07&yZF{IQ2SyL zw$`^`kSbV=E%U8DNV{VRw!pV|uy(>yY^HDJl2&6G*6v$Y(h|%(%>BP_)ghXP1=uj( z!l9auh1dY!8bdV+i?E))CBrlti?RP+`@cCvbNq&D2$o>~^39A;KP<%>*7CVUQCAzG z?pTJs;@3qZ_5Qc%-y<9=zLiGlZ7jh4;9ELMuVW$hTi-fH>m@A0erYW~&ll?wj8TTg z*h79@eT<&L66`+T;<5S-mSR8ltvpVTVHx&)-?DLf5Hr8v`+vSw$Lk&}z^?W!oS-|f z5WCD;o<$UWXiU&;ScH8S+dhjZJejEL@fbe`-#&{dyfR5w;t775Z9C5*3NwK&!cy$( z)>_x9S|DK=_EoG)77?}UOx!%m@t-f(&mu~b)rklAA$XT8BAu)g@en^SuMeB&&(`-M zI;Q9-Ji_J06M1=u{_!kG%O5S#8>W2RPO5jM%UWR@0TF*e#-D~qVi)*LLshFIIc z{%-Q`iO%Pw+lotV)X*ogZP^W|0kZGZ;EY57EyT6Q@!vQ{~KNye^Xe!r#?J8 zZ=Wmzf6;ntz7Y4)zwi|MqqRJXC~C{S)xa|BY1c0MQDIqcy@Huv^ZZX|Zkg3SdL9d~ zN3iX)h@v*^t7q{LPhBlpL}87-dJ>DUyPUaplYV*}i?Q2%tMu2;u>`x>TAoD|ZJGhP z7fZ2ge5(!6omhrl?%E`aDC(kts$u3g-2Xds>rxt|8?gX8*SB<#uFC5&?*E;UMHFpz z4A!N29oFg0wHcIjJ{DppISXxFy`*!n2s_4^tBZ%|bS%aW^Q}Bo-@p>=AZvLRQLIZg zR3~65c7U}!izuu*Oh;iEwlB7Q7EyROTwlV?HlF|EmKoUE#&CTe3$TwmGqxYe2>l-x zVjFy`jMOKw2wUZw8KsY5F}4`nK8q;Ur#4Ev;t4*-uZ>1)9hPEKt>xFZqOLSX%drfb zfNh^e6rPUJ0^B@L{*P~;MHJpKRx|JbA86aovxvfiaSE^y>t(IYEw?^SW3UMO@G9?^ zjmK*!7Gv*P%d?20&GH2G#}e#KYpwgmY=U}VDfX%}vWTLtI#C~di+*A+xHicm3JWLc z9n8GI{lB$5izuuyNpD~Q_LQ|eizqA!^fDG=Pgu*dh{7uE+J;5g!`AXFqA)XA&tfr_ zI&<5lHd#+%33iun(G)$7rP%E)v-9-QRQ(*wu$!?iSwuQj_u%G5uK%vK(~eWeG~Iy( z*kyUGpG5@I^*ubq&(Gb@BI?t19UkFl7v4NSce*Sho}nx77~kTS{h(cTd8RJJ66_Rb zfwgR=s#uC0Z>^O@RA=c-EW?g$nadPr>r~AAh3kLIY!=a&t&_0;+te~!m(0E{ibpbTAfUdtqI&h}t|IfG7AawzhQ~MDw*TmSSstD=kn3%dlm> zr3X8y|kzi%C7?Suu`Oy7cqT8)KRyKnV{T7pH`Sl{ABnuo>MFyG3HH62T^0lsC6 zH3>_xp1xI=Xf&2#|J~}H1Hz>mf|-}N|M#u2RQ<33YxtHdQ+F)HUh%E6T<@Pn|FGwM zGb{8q7Gr<#t+qn1V+ro-_{ z{n)o)jUK~7?EAje*XThk!fvwGI?u;zbq^L}SNnD4b-Dvfu*-bQ*6B7Z#lGuXb-ixD zGVI$sTFrz;TQTzr-~aQiVRQ)=VDXMtHA$PkgN4|M)>>IarJK&iBJ60aOBP|eYYQIZ zU&gy+5w-668lK>vw{5qwh^U8-!&2=3thJ8OQcoR$W!NXN?X!sDe4h5y!MJ&q<3C^S zc3DK>9ldk_9^kv++h-Al2fg)aJjB=J^SJ)JQDC-cP*qfElF0Sd6`GEzcr~zSjn+50+rBTif8SqtRfsVJY?!wtW^+)Rszm z?@aoL{~6ytizqxT=}p}HjpzU7KGTi~Sw!I-L-YzB;J?DR&msyBhH4ug;t%7+JxR%4 z*Xl#{EEZvD-gewd7JsFAvwFc!JmR`t7oa!n5Ie08jB7 z^7?k${^qUC`sxV%1kdmU-#&{d>cf$`9XEgH_kVf&fn7dXMB$B*x(N^PD!zRdQFt;+ zSK%RkMqa;N7EyR*v@XFTJkEW)ETV8TM(5!%egdBNJtH z#aIvDYEv}|ORx{F^tM4XO(iVF-tnz8U45|(d&9SMy1HSe!Sg?S>zJYcoWWRP+k6XV z>YrGVMc~iiU9yP!Oufe6MEI}q{CrbvLzhLwv-AQU`_+I9O;I9Cs0=1rde>C7#&F<19t0d_6cC5uSr=}tVv zFL$*ri>S<34U4b~oVj+*0^Nwk*tx#d7U(K0!OpPO$|9n&F2z!;)3?$>osVVMNv=(o zMWhRL4rbos{@x8@xJHVN1GhU*jun^nV zS?I=bslJRwSjCyE%a-a3Sd8uNTXmT}izV1j)>>IaxLlvYQf#%gRu<7%u8(6Gwgl^v zMIhOq%&n_gr4?9+P4um{N(-?F8|7QHTC=ejD`8!- zh|(HO#uL1cUz@JcI4s55tmW6Y;`r!TtKnFN{rd`zz0vmAYO~j!V4Viwrd#L6x9~1m zM17rl-~s+Bo*#RyEFxa7557rXu@|hhHJ4j6Wuten2z$=A%;*g)#-6ffvxr*$F3^{; z1bf1oy&|psy~$4LL&WYCDU4pq9T6^gCF9otW3|m_^j` z_kn&35AmaN-!Y3|EclUw$KcG=YdIu=W?{hbBY z!ht#*OR;^d*(`$b9H@h_4BM+^MW(=b4$^^`>Djq)*OnDo1miha`(XjLu4T5)l(aV% zV#}?y_N`h;dteb(c6H4xg7F-pov|33g?U*-&6I{}HJ;$@wzhQ~q(ij?OR=%Obqv!y zEW?KR77W*P%=GHqIKa31a81Gjtfz1B2#v-xO+j5U0# zj#76l!Cvt#9If|Hr+?VaqRvD|8umJm|Z)Th_EW{r2 ztu{{2U=enoZ_#-D28*#D`&OEu$FKzZzHjLSJ&2{)P1fw(f@5-`?!hwbYQHX+q&qOv zr*q?FzSSq`HY~ut>suV?1}wzB?OVBBTd@fHrZsz>=NN3)C0LBbJJwZAb+W#LCD@6+ zg;R7kmSRWy)|jHxunhZ(Z^=}B9W#A9H-6E#$~1iy3$V}mW~QqH3$agIYh@9&>G~2D zVV}UdWD(H}ZNg)GH@r(0QJSgG;0eCow%y7i(wW+brPvB<_86_2j#=6h%dmx5mn&=9^pgq{2JTJBC@%f zip5wzYpwOJ&eH@e!Mghv&eupR#ooVchx0(yH0EnCmSJ!EmMl;o%=GWv__}YEvf8i! zd&xJmQ2#!SK4O_~wT1cz7Gcl$7A?}>uo(M|wN@5UTCBfd3HF$^4bAIl)ufB{CoIJt z#JXe=9ZU2yp5Z^myJQi;Qayp20i7Fvn7f}v)R*cZJiu?kyJQjZGTn=Z_%(QOPg1gT zuJUr-iAC7udE0SwyjxjBwp_R2G5%dVKZc9?Zgx&qU7_po1V1OQ_p^v_rLM$N{ItBj z-L~(th{j4?h-dgIc$X|9S*3GvGq7{xae4cJUH(+N{K{&56A$pi@h(|}S)*^@A%0L^ z?`ILUHTo(Z;h)Rh&my9=IueiZPvLprJKcR(X`K$m671vFT3JN8PMfe4+s#@li|AOd z{jm%SomGCX zlU;4IFPxLRX(<+Cc39t9Z*{{W>}B7gKKjoV`iX7xt<+cl z#1iaT-_pMNJCXe`ZH!qog07dTfM)2j|JGhzQqIdYiu9(_Yl7W z_p%7abD$pKZzBAAcz(Vqj?HEk!I%zIipThMcwziaF-NZs(p}iFfGh&P!g_1I5DwP& z@f5qzn$04Z?+(^YScX+yyKX;9O1c^|LpnE}>C7#&GDMeQ0d^|pWf6?&P@Rv5_(`s| znMH69GgRkb5q69-*KRaSr(-d8m~W-w`UaL@2U)XOM9rkbbpn=R2l&=8LPud4wy$fm znMH8_GE!g0%uw$Cow;?XkJJ~i0NdTSc$7Yy*9F}FI}7Z1lt=4Rc^$UenQJo}t&d|N zw#2vU8105d*gW6Du?n#mo9q4VJSA+nOm32c+J5wY>01Wf~I1o zo$J4EwF#Pl1z30AqKO)bh1mO-c>8H-k_KZD_O`P=_FS1xQXeeFUU$~Kt})*Y)P^P4 zOW3yV<7Vgkf3fce?fUm-`iEtOjs2h1+w~7D!=7<%y1vJg^*78+=KAkjd5Zpm1=wT0 zWxW3r3$X`%t4`JLun4=yw{V*3Sd86aZ9`AHzci-l7g&PbW^HRXYsqvyfTh?CzEx)E zZY;yL`fZw-`XOefbZ)%FT7HZc+pIQIw_*YI9c%VF$UWyQU5AC(*}j!#>q;!bPV?JL zXX_#?#=efV^-yvCYX9igqnkMb@BhRT{CHctt#@Ltlf3^EOR*!dZQaLgH-^0b6U(qe z3mg4Ei+TSiW~OrgU)ZSsS(*2LVga^)VI%)%*?b+2h1fn=aW3uFY0sq#bTAfSdpXOj zg=HOx#n`UawjF4VoAOgQeJq7dN+OyQwy}XMMSbVkYd|_^!2l-dF4s@e1|F0_;s^?)WUP zR1Yk~UiB?osSi%257-O7Rafa9EXJPmEnKZPumpR`nOm2}YQ2o5*b~m&x+H6~4a=~H zeXFe1vzXaH{^y%nrzf!hyUVxQIz5hs*zLYW>-BRi!ftlfJWtm1cYWTA#n?5@n&-*7 zNsaEr66|tpyYpn-bhN34rPu|9wa$}u6Liy!ScaYJ+HCf{ZtC516=rrK|MM;Gu1m21 z>)g?p&-BpwScskETh>G8U=enVZ`GbU9gDHUthLUQVK03HOR$5iwa$}`UOE9wu>*Wd zdg~}G!}j&ttn|^BF|#w*e`~Gtr0J_KU;(zfwbpsE)>og!LTo4BqJH`m7GbOXHcS2W zaV*A`VE2zdzc^2J{`RV)i(?|~uaDpfKG)W^&XXMjv>r>bY1j_u$zY&XU>P>CuIZFin557rbc#QI`8oF}uv8jnR-H)okUhn6$~i?RP) z)U0crC&M8cgeBNNv2ESAJ5M%-s5h2kf46n5^JFqq2FtL&T5F5!d89H-?{(6@T{}1a z**7y>Z(#xUd*5oq^%@ppzxFK}p%<|TdlcK@JXsp4KVtu!#An|41Gctxu1rVjDg2X$ z`8fWQqJF#cWydJ}65oeoEX8lP^{sPeFj_yyzeBx=$p5Uj&YShox(5%itDU)XRXj#_ zU?FyyZ{@MN4U4ev`j+wjPb|j1?OS!6wqgnPO=p4K_ThM4f~8pO%&kjfyuO2F*onR+ z6LdCaHj)4NR+*^NumJmtZ)TFdj)mA4eXC8s{M!8w69d5i_4B|MRUrMSEfa zw$QhDs&>IbY_@OZX*HZ6-4{7BgSy+-R(|&Yu;Y|A__Idlxp(1FiF?nXLg>h`r@oZMJ%15%!w3R-4fr z{Wqq6*o&@isofV#bM-GQ!T#v#dL+%cMmkpwEXAJ2Hcf5bUvF!3V>nMQ;~D-0-e$HG z`#@WdYnuB&FkcyNzDWL`JNJ*p`LKtrug}--@c_R!_qJ-&jjfLtsE&vD9rzCC(sEh9 zz#{B6XIW=+y|c0&z+&tMYpru>b)oLY5^O8BiG8kUH?r##^88Oc#V@wCt@CMPk#4~< z>^!&3$Zp4Ev986;L7f}F)pfMwVW-x_PR3NxMTFV?oYdy8b97GnW62FtG>#Ws)acB`z{96ZE_xY~Y2|LevW zO~oRt-;T?yb4JtzEXKO~7PV<4mSFE+VE6Opc4O|^O@px%d)wN!KVLbN?akb^oBCiG z_Bz(q$F6J7VqKZPb{Eee<^2*||7~rakrlN;554yd`icG7UuM0B-oirc_tx5jq*)jD z)N5FT{W@Q!+dgfzql+4vEebKLrOZqAnVW0D@JVYH>jD6Z#yRrL0 zHbh^-66_OLJ7bk^*A1>8)uGyir}%F8cKLKYe|P8pScZjIr>$*kv)iCCOdD}?8rOfj z-1|SgZ)mZ9CBwBR7GMkgb}J*a3pR`~4)IyGcH8EGi;7yq^FQ%@SjPx&x87#rX4|!q zT7t*eSl^;inujIWFyBg}H62T_0luZ9H3`eGp1yUA(P+$^PX7O0JLmt;-OY6g#%c%_ zVE@8e*G^-Pv-()|#Y6lJe8bFBi|yap!*0WPoZ9dRf62DpZi?UZ)bsau{`>3n6U(fz z4K^0rC>yVTU~<^h{7*c^FSfPqvE6Rz6y1Vl z*m>4A3_qi}-8!b~TFjim^&e}^g-*5Y20Z^05Ae;lw(V3~Tc4&2un_y&j>czN({(Nu zVaNGa=J}skj2+=yHbb3Qf*s;pb*4_jQY`W<w#->`KP7YJ`JY&VE%2?x^FOf^n`w>nYd%ig zm(15{EW_IUx(=TIiJ7xF{#_l{)S@on`JY&T4fE^jWlhIIY=E;(wk}?%NmzvS{C}+7 zd6*Q{8Tb9u(*ulvz#!{@QX}BfYQzx)6r~1mUxE$p8VYd7L}@fGXsiMECDMvp)KG&P zU}DrrToR=S?$JnaiHSyQP@{24a2EyV{r*nhgD}Z+J@0#6?;l*}bMAA_UHj5iJxyC% z+cFH}_djWiY5Q*eCf9x5T{V zo}#U}quu{kjtREmsPb#HC$v9Kd)s$J>{@uA>oK~#lJ=DL->LmNdp|S0@o4cy1csFe=@6Gk+$Cmf*E>*VY9_2@94`^SY_O~y!_S-A% zEB7ehPkTuFjefkf=WW{YnDS!UBigTNn|9m!8n$P7A#E{jm$h%p_AK8*TS8ltwbi$c zo3~eaK5Z#&=d~Xf^825(Wwgy|-?nCK`4ZaP)ja>(v~8tJT=_!U0@_Y!-!_ro|D-LX z?MJk=dtc7|Y*qOT+9TQzSNj%~YdrS=T1VW|R+UetJ*NGT_PL{cc`9uQZTq%w>rq`k znzod-uzg!vU7kc+M%(E2ZD({SA4Hq`c~h~0wp>=3AKPDeCfl(*f%bs*Li@2RJC^sP zEu?L5`?k1Kc{kc3+6K078^-T{(iYR!yM0?$Q{IlYgtl(&+vaUnuBR=ftyBB9uyeVN zwv0B{zHLqC@)oqYYxw+s-6rP*wq=)cSK0#FK5gGNv1_>lZ6R$RT3cgfzhXOXUjFWU zmXEe~+QtR`xMv$~UjB-nOV|!#y0F%zJ#`zw$s|TMg7Yc(w5M6Li@HJb>(wtOKJO2`?j>MJd?JJwjYaZ5ut~Ua9|oa|mrAZJmAFhUeF8zrp1#X^Uvfw;y-k;Bp_@V%pYS+wQYc zG^E^}wq1GllhFP-?YY1{)Gc_D2v zZI`tl7wufWg|>vYCfYum)VO#m=eRG98Mj#L_N(dR7dP?zbM1@?i?t2D-634-dJHSC zpZ(OLt7p4jE^At~e)dC)UBA0NeK*J7jOAU~xp3_l$5bu;Y*KFVSJU&0T}`-l?TqTh z-_Gi=xDRbroa?iEheAufs&G2fywg?JE*mpsW@BsXZSp2jjNpG$fF zkEi{p-=uL_t~O&H#xp*D{KS{^Od!kmS~9Dnt38}P7jENbF8{7|a(ZGoI?K5c$~XVj zUCPSo#|Kq@{H_{5B)oRx@%wBv-jBPh;rnr2E8~W0+;;nIGTl)djoW0pNALRm_#u_? zy*2*iBR3hp-A3d6bWa@a$N6RNI=mdv?&rOxlB4>6EPLOLa%?gWyN~#I^4oSS!RepGqV z+q;xUb>g>D*=~M4+%&TMCVh3T%<==5^gqcR`giJT8$ch&K9}t4)?PT+&2;;^$7v%_<=94xJKh2j%m6xjz zIg<&S=Dc^KoQZM{aXqtq-z7^o%9-W2STdje@;+ky^3}Bq*f(?Ev`!w)JdeI^^oHeA zzq{#E$1-U?D$Ce^$wHg9Yh>DkUG2DmZOc?w`1wZDk8PPW{p)S|jkZk6J&%}jvP}HF z%B0d_KR;o=m$a=`Q`fP2E9>)?&aUJzeclOz);X+O`7=d#OL?gk-$VCAw}7EF4OE#`o4nwzZ9IYa38Fz_-;EhPAcr z(biU181LKq7q)L}8`;*@zc9wP^($;mo2H{_u4doz^S^Fvc{uHHovW?0v2(Ec(Z14s z?aDi%cX^amEI+NRHWOv~!Wf0Qrx z^6di(-^^ou*g0-B>%h0aUtR9%+v^IS((dQqkFTq=ztf>y?c4hoKGA@?LdDl>iRNZ$ueSE$GNPTb4stmy-YL9 z*A;Hf_v@12+{ryA`Qx8{3-bMEG;{oEyyijE{|)gC+wkGd%lEX)_lu44U8Q{6Rn8~P z8|723I&v{xf4%z!ZCVFft|ou{)h*GvWj^uUDYcwBryhMwxAN#Cx|e59>QTP+hqYy` zqxs!9XYSY2pW}7j)H3~q>FPMkZ`XE?uUpbYuA5kgYCnN>DIPCZOWD)~{UKHa}x3`M$V@e8+A&KQ$ZW^UL&VC5K;* zurlsFjT^J+ea|=NRMv%-!;ib6?)&+->HY10jQgnlxZ;5C$JzA$q;Uf`UG6vAkLxzj zkL#WH*N!!g-w}F+KGS~Id%(D_JNt9PE6UT)_0%?HANI%1^UA|I+c4k%-Xss7g$I^b zwB>oHl4phTY*#aB<2;YH&-3P%8_&;5ck7nr``hw-UdeO6@&p|>$+KvqJi3?H{-%3* zoj1PXUS9X{-_F{5@%q`%F1qa;*Q?4cEIzxwwXe!Ie0h8E8!Nkg*V=de>}CGG-|@ac z_xAJ1zfk8g?)6pRu@2LGe4bD{71c0N~lhtD-yE-lX|t*tlmS-7+2A?K>tuGNK>R+~N#fc{@kpT7Tp zPQUWG>T&CuF5@B7&oAdjc zEMHqa8Ok+eqq^IM-`Cry9#Mad`bm5Bm~8&T{rHZB)!f_Z^Wr?KS4EZjRn-0G$6iDHbxZS?3%CztS?{E+t*&Kl z-l(o+y{)ZIu9z`5TYW0aruy|8+AH<)hC07fU(fIBZm1`|e))zv`Dnk`>Qh~%f76C~ zrT+yR>XrWIZm1{4koiB$>RPv|&)m>XKI%VKJ)n*E>RLCaSbIx%=9l&pR1fcBz5mA7 zr*71q(SF=U?W!NOQG3YxIc%eL)elvj>FKjj%rp`2}37xRYBtmm5Yq~-AY8Rx3+zGGSPiO<{8 zDJgzK{jPzzLsz%uWPN@?%;!vR^Ep4Eo&>o=tK8kx?=F6<`kBl}$b4Ox6#u3AJk{AA z7skbRsrz}jiFsJpI{96$Eq*zeo~HQ^+OamSF7=_P_N8SY8!j=JJh(D?-nJ2tG!8aj$6gAZ;Nl|YOhs%$4dN~w)ikt zdzIqDEAh+Q;-g&cCD^Z{;X`INuWg;I{2}i+6XMDp;oGc#jDKPA1m9@!6wkMKhRYUr z>|Z~(c!00ORcYls6fqs;t;(_P_^0~Kst24uBBozoSuX7_I(}2?%6r!V*B3i*UcKvp zr6p}kzwSBDo$AL{_9t!26IBnBVsZ|jx7cT=QNOz%`)eL^2dn|VEqqLuWR9%|6WcZT&vXI%&zp#T{w)`|xY- z&|J2xxE=M?91F?I?ociF5b9b-$(gxBwah_VzJc5LeRwFJ9X90S9N(|4{fVLed7$oL z*3bUqB3aRd@1HTA^i!U#@?28SBy(IV>$zrf z{a?GAuA91kR)=fTpM;|aRlAvbzV%Jt1$9UBOoV%k5l7D+G-=u3Dz2%$3(KZgjaL3; zJa5qNlq@^Yt({2jwrgV*>mU~-#Sho(oY%XMa8A;`@*Z`+&bTJ&zHq-P&hLHWRd;O_ zUR%X7*5sDna#>Z)m2c;3mgxE8O8RpxpA4OPqF{##IamV(X^h=g;;UPzj4hA^ReMwQzqi$C0(kJqdI^Kf*~SL-z?j^?@7 zxG_B@T|=B_ZJ*SC+d7#IyE1rdm+}W^x%rj-x?h8?8+XyiADdij66y=7rsb4xyR2*ZSG3E`Q*iTZ|h$Zj9?0^mHSl9{kN3eRQ84;(jjNw>d?1)Cz@q(UPjpkb~qn~sgy4=~X?Z>^(bS&r@h>n9d*0HXu!qIV|vd_Op zUF%iv8xoGaka^eG28}Hq#aQm`cpj$X*6?-Ih8x za?FReKkzHaqq&c-%ma>@C+_3>w)pc_-IABj|QKI)m*DK0nT#~g!-e?!czZ5=La8-J;~#xVcv~&^zi&HS+r7V=zuAn|8wYDVZHMsh(BoQf95}7@#x7|7zB{y0{|)`l>C|)Mer@wsxrchh-$UtMsUPPytwX(+)x8qWc-M^NdG72F z`}lXbU;TaCvhJ1D5Dn#VkHEemBd_Z~Hu?&x^X(y!l(ZZ{qV!BiC7fyz$MH z&*-abdDO9TP5ur2Qm(rTDF?Yu{+gT%6MrqfmY5$mp-cH)#;M;;zTdzS$Nl79LA;86 zf#-6=%m2_kaQyoF&mi&F`*Y~4=eo&|%CT}Lb$>6%`)S?F@eF*;t-JD_D3(29ImlES z(dSFfRX22vcGGkFr^#D+wl{Fe6L>Uzbq};#?hN0)%-a1v`Euv-@2T(Z`_vV33BN;H zmFqm#^_t1O74O0NF8S@o{ZH#s{tfkj{<)CvEcw3MZRopb&FahYE$2Sf4cWYWJ@r{U3tIYvu9Id}^;$BS_n`BC)W5K_ z>YNAW|17+2epT-!^Q*R4(zYF|>~r$AZR^aoqrVU0^U1j2wYr#XIF|ORu{|cWtz3& zdu#kb>*T-CzJB%}{4t?@d<%{V?c-VxUk%{CpYQNbVm;^{(dly)_k`VT{;K@^ZC2U; zdQ|qmPrvzo|J$2!T8^)Y4RTwS0?zlnLfik29A4g&_V)YVYxLFe(6;}*Onu}1Zv^}9 zZoGS4%CY(Cqrvz4VdiQdr|(znha>aB$SmJ`$x!lF_QO6)w#M3LZmimFB+ItavHiDH zj9lvD_Ny=Vr#`D&^`tYa&VE4a_^Sb&OYQu>e)bc7-RjtX$=2=1l<{}puiNF^XMF0{ z=fnTCK6Om`?}Drg{5eJEoMjhwTD-1QIkwx@?ZYgC*4aIjgIrBz-Lmd8Vz&@$JGTGL zIQ47x{kAIcO_a%fVq3SphL`_Id;4|Ed#z2@?FG~~Ubhdwohy&v*-5X;yS!C;{}mtL z4&^@i#zFng`84AEF{s}epSJYi75Mrg?wzELQ#2#grBx>oDUju#;47c%`0+cZKpjL7o5END1FxYEqPb- zo^Hdmy7nyJtL5XqY#g6k^*m1ZgSw~O&vNf!xtCk+CQ9WEXN>YWf7w3gYdy<16C25N z-_7?e9Iej?+{5V}p!w;>(ORBlMdRp&?=_Bg+;cVm$;ub4e96kcTKT$_Z&~?va5 zo^Ok(Zq{c0sb7HPm%b?HOpRaAbu`chH4jOXKr_Ee{bqwfuIEv-xe%Eg| zt$$nJE8Qag{)e`sPe1mhFMo98OGnS$V#%w&9I)i@e4iz+R{ivWp?r4hL;vI1_qh)& z#+2Hg%bAz9+~aJ0|BJS9+&gPNRxuxghPaZpjn;cx-;vgL!}QPZRc;`red#U6w=JKZ zmvyLeI=A_I{iT)nx1Z>|mN9Q_$L^;0`_K5}_)gA=Uy)1u=m(VRXFvLXyf5fG-qq@y zxEssnxZeMD>%Z3NzCfS7^%-dNl%Ra{zFo>~?zC@@%?~Zy$$9lI zjw?NT)U#qeYr2i)e~Np?x8_%__p90O^lVt4y?;lWfB(z%Ii>ypb*`aH*37JY&Q~9u zi+@F2=Ya*3ZJ*EEK9^tP$J8x(n=$HhgYQ%4?*n?frpo#rGot)D?d{JMKc(-+_W>p9 z8=ott&1rn;gsSjbK5ra4o%?^C7boyMCd=mw$I^G|^zdBAJHB z#guK^`7x%8SMpr|j_qDc+)cg9v-Xaku%rKi4^`SMYM3?k{xDq3;3xuj|m4 zmsQT8-`e?ZBG3D0k@vIpQ%350QVaA>mg~`$O#8F-tq;!Po<-Ly|6Y;ziF&UWZglV5 z{+;6gm-mmp@Bhnpg?jI(`!>gN>b;)czvZx&`)}qDziN&!*IVBw#(d85PVfun6hG%Z zhwq5+|65qxaqK){4)Ahwh#!}^KacS5Egs{C%?Vy=PO;wqsegulV{u3QS7tu<`uBfY zmJsvI>c(6^eq0|UrG>uE{T}rNGG)TQ9BsEyZ)9AmzF|_jQSITa#f*BBOGJ``Q{Ak{hP)+ z&Qm`%2Y8muyoC4+i%0m!<`|!9PVh zEUx!@Kd^X!cd&Sfx3hSJhbkU3|2Ws2l|6UC~pMb^_A)$ za2(P80?u{3q?l;ziAhQC|5p5w?M8jY9~b&{srP>w^*>U&F05yb$tA@AKPGEh_)%OR zuw3{di^o{+|I{bJ_sh&{itn{}hVQ}E%Qd9|NEuBpAd_w|A#W6{Ft(yb(P`|u=-?J@Bj3h(XN-K zk(Az6{r$PcfO=*Qv5t4Xb9G}A$5cf9Z@50@*v7A_U)3GO1ivJ2&2+Hd75e=Mzu^0D z9Ppm}Ig1DQX*o!WA$|f+b^Kj5&h3+wQnmHFq!?3w4Ce>4t#O_<&HuyJo)TM%{W9SP zEbjVn{g)XR;9ucp)GhjWS{^?A5a>kBt>FL0IBW9pataZCeWVovc~bB1GF@3@}uf0Tjsik0H25R zG0TDT`&HJ3-s?rwXHmwKGbwZXkss&i^JA;0)K8UJ4;em5W*%LCuKzyH_krl5N1 z$&E=d!pGpoka@#L;`)eb;KO8crnsp7IKw}b1M=7Der8eWK-KkkPSmHmq_i*fx%3UG z-%efIa2&BFViB>icq)!@6YYCYZ>FADeWY(siYfKs*zW^)7pxp^0M~z zh*IwgW6Exn%9Y?QSjTpX^{z0ZRGtplZOQym29!C;0dffBc*|!B3l0{De8f%grv}{@)zn-0Edp9mjsj`37n&%7o0N4OEE_!#wx zn~E7e()XWNbVE4)Egs;a#Y6lZ}Q`P%qiCUKdqAthi1n*+|wLjz5i36 z5RWuRc(^&nyOv)hK_-yGnraeh1U<9x5m`C?18H*iiOR%ecJU##Od z!M!Y=;#zZtyICK%Eyusb16*ScaR-@Yig1-V#;rWJS3JS%yss#xILB|*d`-PRWL;7J zLTw$`mzeKQ<<1us1NyzNOtxt+G>yzPsSRc22T>QKB3GiR7 zPl#W(J`w(l^@;JHtxtmgWPMWnjP=Rzlh(%-xc|340bXW(Li`8o6X8d!PmF(OeG>dz z>yzU9tWSo2ZGGGh{QtJrC%|`GpAg?+eInd!ePaAe>yzMHtWS#du1@PY!`E9Mw_{w? zdp&K70AC|(U&U9MBYe3z#+R5AJXel6)^Kdj@C9bK6W4!pfX_CESnvNdHo`N_G5(1; z!86P$KG~e%6U^=h-2a;cJVn<0<73ScKFS>9!_5hvWKQu!bA}H#yPdiJHwSoMbBHIH zBiv|?@m}Tx>-}499_t5>GG};%*$w0V-yGnb%^}{=9O3QFG2X_U;KAk;2j&cKX?DAC z{F?*ZS9Y9BaW8X(Yt1q4W=?PybBb%s8SY?qoMZH^PB{bI$}>ZsAFne#q5U1#Z~42zi1Be z^X3RYYmV_#<^-=Wr#Lld_%X8^!RLQ-fFCl4_(5}o?>EQzUUP!=u1@ou;zi~R-)VNc z@vhz+;05Lo-)fHV&E^>2U{3IL<`mB}XZUKf8yOd`FbDWjbBHf8M_BLwG@mj4nK{9; z%_%;|oZ+*~ZWRB&wK>42n?rn>Il`xyV|=1H!PCttj?5W8&a7|49BmHp5#|t2Hb?kS zbBqr$C-^{fiuL}_?>~5Nv)er`j+3i7{_t2iNQxmIgLR)1VZMA*SwDC;bAosE{n@^l zXHTV_tu3RzquK4j@ox_BHs%lyHb*!x$9PL~g6qsF?rY9)FS8rN@ox@rH*<))m?Kj=QLks8N~r(K z#-#Y4<_y1Wc6)JLn*;n0bBOU=f0V0P z2E5G2Z!5-lD)-F4r{2_+WuX4BjY;uRbB6W)P3z2seEye%g~b5t{abU@++v8Env&8Y z>Qm_#QRf|FWq-m8%?Z}KI^|07t>z5hY<7*6cXjF$;Oi_N;(6u>Uu}-@73KtAYEJP* z<_tHP-8hba+~ksCfM?@8(+_dJN9EpB@BeboupGo@$uZj;vmcaBSG@=8OZ8`x(hTa# zl~O+$>)tiPC#WAe+<314vX%!=F^5?13bj8)_$Z6V_;7QAC*j7B{CFa6S<$H>!w35~ z`6sX)qolMy^{EllpuUg#b!VCIcsa*3aE|30tNI|8hk8BRe-CR<)E*?Ik*ah3PpLQ3 zzN@uoY9Ge&uR8mL+q<&=>|pHy_3h+kzw8;}p}3x9j-!zB#b#sJYGspN9bAnf4wpB63FIqgq z&nwP)bo+DtR~!fUDeDvB73K)1<`_SQl{3MQT0F%MnKS&L*&V?3-yGn3%^|+W9N|Ug z7~g45@IrHn7nn1AtJxjM@ox_B4dxJEXO8eZbBwPxC-@3;iZ3;1_#(4Ah~wWJ;Gda8 zJlh=MbIdV5%behnImM@&Gklua9nAIL9N-hpA)aoIaAc10apnXcZBFqK<_u3ZyF}@xgyX1%mIGa9OBjH2)|{H@f+p@|IM7@SIrrI$?PWa z{LdWV7tA4k&K%*Vv0rca35zFqx%cBeQ~Wrd&UbDz{Pv*AI(bCx-|-6olllCQTXL*3 z{98Yc?S$|1e!ORdf2}@T>teh_amFV2Zp9g!;yZkQ))j8{e!QnUjL-k-!`J}dqBvti ze3RmgjqvrpKWhnJ>-~7o1Ye^*j7{-XiZeFDmn+U#cQ~K_eShX3&-H%1XNY6y|0~Ye0MAgIu^~R$_m7JaKEeC(o-v-L zK8#K96vY{v;$szOY=)0goc+xm$@Sm+@ty&mq&|!d@kGTL8{vZ$XKaiQ@co&8ystOg z8Bb6j#%8!tamKo%`24RpV*|X0@6Y_>QQmA{%ooJ`*cdk`&e#O+tTTixamFUN^-JQc=M=B=X8Yo`SYzF>T>lkkY=A#goUtKZ@5d4>inU$?zhxo5JlK z5HG-*euQtuF{R$U-0b75L+Ur+)Wlv>fwzwK>37m_vN2Il>oVO)JJt zIHAev^kHwQ`SH0px`_6O>N zUFAG_60w;236y%jnBZwx_h~7fB8RLmoFnHkzMd4_G@k$aF`XI$d>F3$E(O!0&L7b5 zJZu{3>TXj`>%_{&OF$ zXWkE??vD%V2bwdypV{TOr!WV2oH@i}vGPWEjKyO-S`Lz8f_Ia-{-=0X>yzPOW>>{M zg*m_l%zXLplWprwe&R!LGt-JOxl4hKNvLmWPH~+%!+p&z&pE{$;97HtyO|^0#T?@r zbAmgVQ(R@vaO)S9b?2%%r(hj(nyt2l!cYh@Ub?cm?+R5$5|-rDbX(PeT0<)|TQ& zY)poKhjl)4ow%pKjl=?cpY;jxugwu&qCP3x882u5yoU~&rqLl$d7+4N1O|ok5gsF2KXd%h>w?p#D6Dj zDz0Z*F>$6}I?nnf)Q?sh<5PTu;^fWnWSKFpOJ&SN>e?;=^_HrnbdcH^7gFEf9N~S; zF&>ZWIWH!-XyNA+d;5%ulW#yKO#xi~2erQS$eqQ2y*S9^Sk|Bm=pGHn@c1I&(X*iUAC1-Q4x zL)_CG;qK-bca>Qm3Em7donneRTAvK(abwK#bmJdxncKY~!0SJkT|q1kokGle<8@0vrr+8p7x%rSn$oZ!EiQ~atq!!Ma#4?a_v z1N?$H#Lt-{{Iog7PnZ+D+??Xa%^Ci^+12t)(H!8V<`6$%j__~rRMu6De}(Ja#l-}- z+;K-~5%uL4T~th|Pn~c_=?;w>!aG9h&FUMn4LClhvVYu0y*XvSqTb?~OE;^%k$p?; zP0gk2)xMnXL#aKtuvAw2)*L_7bGf8+HFa%|g!&cUT(8s?B&AENEu%gcH}+17ZVSd* zJir&2Lwue&!e^Ufe5N_UGjToBOYu)Ep5Ym0*Ne{-<^Z2y4)HW|gr}Hee5^UaN10Q6 zxH-do+21dxe^AwIwy;eE|9o?uRJqdCQUnKQhH+4bQwg*m_@%pq_eF2zx1`WrPRMLXZTZ@bDQhWGX)nEKJ?1Rr5ev9{+VjpJ`9GU^l6#=VFer0w-c=^*O*{(nGyf82cY#l=u<%-7!P zoA8;DdQ*p_)TsJCOpp3rGT$vt@E#UV@hFRDc!b3r`$vPt1H7|5n0*50cptEX)g$Un zaZ=iry7sApLc;PpJ>iv0qXjOkLahV`4G&4=L+uOYr-+F_-x7qP>gtc{C%w z+Q(Tg)*JhJ=}omUUjg;k<$!77*Sxt8!LL}K7_Y*OE9Uy|qcyI$z4T|Rr?hDuKS#Yj zsC+N2C3k!2De8l_=6p4TXNJ@@zsrdQ)R(Dm71P5_Io>H!*LN%;>JQ5?_i%Wr#S{F1 z#Z&wni)T2O-d?(gdcJ{W8_H*md}ZC-MJ%9xhx*bMV$Ekly*}m`pne;^gL~W2<{RUdXkb*u@+-dPeYbV(Om}yAr!?biCyYm-#rqPe=V?9Qt^5 zVUCaUtcm)CxRHD@J|E{2?v3&C)eB4KsNTf1sMoVBXHxHQY`K_GZ+jMVIP}feFCvgdCob+?q06vGo1<%v42)^PebgI`pWZ}V~F|Z9>k8s z38iu#=Hoo~pkBn8k9R8k(8oI#+;)7Y5C@bRzrT<3oqOv0;K;}Gh4DDYe8kj4nddVJ z-V^6}rj+8gbK59u&xnn{{+zZw$G^n`ytBnayrazjB@*H7)hA+mV!h)WN`0!fCG{r9 z{lEHh9!sfjC9kM%$nXG-)fkT*YN(B}OMRVl$PcJV)r|PVeSoI(&HLG63@~O@`yG`|KwwLO0Qo31nwn3)4w!9Ulk*KIDY%1tBNsx z`=ej^{pnC*3H2XRrj!R$W|Rj~@}|DHKV?9%3@N{$j3_^&j43~%Oep_D znNogCnNfa7=|=ILBFcdBJ<5>sUCM~^ZOWLkg)*Uhi!!DB2W3Y2I;9)U@lP30{*^MM ze1$Tie2Frq{0n75`66XX`2uA|`6o)ZJJ0_p1Ini5_uUVNv(9N-hpA)aoIaAc10apnXcZBFqK<_u3ZyRp1eFbDV$bBGT#M|eMTjQ2Jt zc$_)KW6c>JV|F3Ozd69WnM1s*Il{xtG2Y3X;DR~D+nO^x#OxZ`pUeT?${gYW<_PyQ z$GEpS!9C3>?rzR-SF;<(@ox@rM{|ht<_J4;jMx9CvTrB&8*_@kGH3X6vm4LxZw~Ok z%_08K9O3uPF@D#a;ML|7zh%zw8@RbM^EH9vACK?C^$q9ayZt%;W%W(2@ZSyliyU6* zzZ>>vd{HN^XY{?Jv+g&l3(uUf+kmJuALd zamL2@W*_IBBECU!#-{i>nXwt3=lk&Ytow5OE6&&eU*Y52U*Ss?XKaKok{KK0Cf|p@ zXT?8LoUtjM?c=<6$LA={SjWBHSu$e-T=IRmx5lR{&e#Z_=Hq;?0-vHdV-tL$%-9r9 z$4%Q_TFh|cNq3i~sDAgQ{(E4{`9DREQQz(O8_NA@KY3B<2(>S|tQb;X{{EuUB(-mU zaWPW+33rx$sP;QAF2-tq_s-IRYTt&>@@hZvuF}40zl-PWYX9e5rSWRt_VQw;_EQ#@ z#;U$G_uB_hpSG~Hhx#w%83J{E1{$gM!E=kD+Na-M+Ewkh&n-r3fBW{*&T0?XW@?Z4 z{IB+e{Z8!}pa0cf&u1vLPi-y@R(ms_P1WAgTpC1us$-u&kZrv5 zvDqEW`#*Dl-!q5!9dm?Ru--Ss_)YKb4GDhTyIVtwU-RC)A;YhD^E~SiKL2}nX$bI( z-klpl{Ji&O4H16UyQU$=PkDE0Nbm}8K6l~No8O4Qk9k)&xF2%-d*>Sh{E&B5Lx>;r z=AIhgkF}j*d@rtE&Nj!@{9UWgKZ}Xw1ExW&?RNz3urV32X0w~f^FMQdZ^0TL;+t?p zsdLx$)+eTZtvSKhm{WWe*4PYRj@_Y^v6on%fcji>h+}hvFTffb;Ew?8(+AqJDxo#?#CRo`N+t#mC}|Qe%&@K5i2C|Kf4*$VLbmg2Y4{n*boOeqSV+etxrt7&Ya-B<`nnB8k^x-><+Jt?Ph%f>Rrqs zt}#cr1J>9WSK)+G({25@vLC0^*O@cCR_5>7+z}lASnsL={F&k%V*j17HF6yL?~Hvc z>v*8g2lBkQnBez(d`>aN@8IUeoO^ML;>v#{$3OP@@#~5!KYmSCe*B88{CJhD{P;y5 zCqI53`~3J>#g+dkj(_a);}wc4KTc)k$B)U%j~|tlA3x;dK0j_!T=|dX`j35nJX>+)$LGk(kI#~oAD3k1$EW)^`SEGk=f|fguKdSw z{l`8(p02p^<49J1e4MQO_-I-A@ew{wemoib{PzUp#Md{P-JL`SDjiPJaA3_WAKAiYxy#?*FmRk3Upg`SJU*^5b`9<;SaK<;QRN zIQj7#*yqQ8Q(XC{^ZXC{{P-osl^?H^l^?$#D?fftR(|}nkCPuifqj0wTyf<;p3nc- z=f}TST>0_Cvhw4lvhw2xWaY=d@p1CwUtymgFIHUnPvAF2u+NWgS6umVA}c?>O;&#V z3t9Q`jXq9(JRke~xU9JHpUC%ru+NXLRJ;TE@ny2|dd&cbAnPkMwc! z@= z@fM0JKkgwbKi*tce%x7Be%#5&$&ahC&yRD8EB|S{|NGGL<8QIgkH40cAAcb$KmJr! ze*7OFCqMoO`~3J{iYxz*`I|!Q=O4eVxaJ>cvhw49$jXoZE-OF&tB;c(zl?o;{1?TQ z|0n!SA@=$4pA=Vq{EV#p_(@s$@gHU7$IE=2{P+*p=f{sIuKcI-{XgvUrAK#+5^5dIi<;T~{%8##==aC;@l^=gBD?k1~R(|}RkCPw2gMEJ7qPX&( z!~H+@`SI(DD?ffsR(|}7to)cC{;lN4FZww7@$=Z{$ImLR{O5B2k9~f;LUHBCsjU3? zFIxFjn-KHbO3k59usKR!iq<-dUYf9&()>53~qj%4M>$H~f%kCv4mAK~NV z$CI(oj}KK``G3a!Klb_Yfr=|X-cMG3ytl0Uc$}>Kc&v|;ACJL4KOU{P@?XgPKlb_Y zu8J!^9wsY4-bq$|T#%I?Z|mda$3w8skLwjze#UQweSSPZaplMTWXgzty=6y!+*4M5 z+}+2?kGo=@A8)3(@;7n+k9~feS6unAlj$4b_5Z?-{P-JL`SDjiPJaA3_WAKAiYxyd z?*FmRk3Upg`SJU*^5b`9<;SaK<;QRNIQj7#*yqQ8Q(XDya{rHge*BW+%8ysd%8y@= zl^;JRD?fhP$H|YMz&<}-uDJ4F#Qi_^`SI@+SAP7kto(SXto--^S^4pAe4PCFSJ>yr zixpS?i@E>DK0m%)aplK}to-;kS^4oVWaY;<`Z)RVeC+e%vf|2r3HSfl=f_tn-hur1 zGFkcY#j^6_IkNKO3w@mY_87nwN-wlc^7=ZsI!) z%p3nEQva4`-7WP=F`<4C^<=TZ zF9vuWZuyeGpTlc$ZnMk%?@WIwGu@c@XXXU2@op}r_+wnfvShf*uji`5LjE6!_td9} zWnz23gIoAN;RD>_J)jWcH@)i$5q@3$pI}<}H9272;aAk>GuM#fRo=OV48Le`$A0|0 z#RL2-)_Mr>Q;PGw-UzRd`G0C+oMOLi@MGo_KWfhKLuS{JYq~kW_nSj}uQ|f^m}9)i zoZvgnDPCyK@B*{z#5LU<;G4}MzQG*f>&!8pXHM|dnC~6<-UVE9CL!tGN-s?&hY7Gw;9)TbAV65Ev?P|cco91`TvF@;?uqR7GfOX z`j}%EABVLqQhc=U)2EQ(BfNVTTxX7d?_PxfAL_kDA;gE^`Y7>#D|8_C`zrDM%n9Dx zoZ@lj439OtE*$^n0FO3@csFx|cg0h~q!{C2xRH5I@J_f`!+TfUP*}pT+q1A8vF?0t zEF;!^*j>cB7X}mSwjIX<$MnDF>KN}<*oxT4b2-L|?d0wwwt1mHv0M+1bz*&R*O>K% zPm46BYoV5ywpmO}-!a=<{m1bgHR{XnPD-1py`J?+ed^riQU~fSIp&4BzMqm)|A6_} zobf}F()#x}UbbQ$R1X)G)>6+E_-?-H!0aBI?U1W6H-U6UyIH zrj(CRW|R+6I<~`7%7F5>lwFhy&+y-8?ozmy*a2*dh*;<0n#azC#l-x6NbD}GV>7|G z}rC3da#jqo+*7+-~3R^%HJ ze7QNrm#9y5uVdtrag#CyoPX5vxmqiZG}f&DdeFvmCc*G#-K z_Sa0jBlg!!yuHkH^&RwW%n2Us%{3DTc;V^cBN^8J^DBc9Jk!JeT8aC3bFIW% zcyq19J=FgRrhzw?1NJH0S$%Y^#GSlz4H>SsxEsnbXz>7l_fNZ4;%~9PR^qQ^T`Tby zSj&^(Pt7U*k2%91ncX%#(=!M7pXLz1ZH{nej`2Ut3I4k|#eX$t_+_)(mS=kA0RPz> z;y;-q{ERuqPnr|_M_lg`|9hy*ES?engV}L@e#9K$-V5} z@HDLJV~VHXj8fOeV|~0^!R^TLj{{0w3lGQsT1b78@6)vq;fYw=BE~tcfd{Fdu7T8b z4ct%doEuW=x&}_rSX~3Dv;9k<`Uf0uJ29SX;27%u8mKzgz)@<~HBfb~fx}hTHBfb~ zfy1ab<(NM8ov^Pi&zuSde z>ce#{z;7z9Ya4#u`b79ObBte6{}uU$1g|ou_(k>6wGBUS{rRR)@mX_#pOSTLW1FmK zi);JHlp+5fvv`CbHOKfN?5}P3LF}(>_)M8!)JNAg{4?yYZR5HB$Nt)e&%yrMhR?$O+J;Lq)75u1PdDqknx}blZNsPFt~s_l z?iyF_S&vtH6Wd_|$0Pob&w~CMM|~atFWM2@KOaMUB=^t%;{W~A|LGml_HDjTp%3@b zlWFV2ef0bJI@-7f()JDi-&SwVtq0Tg1^3hbdPsbK>i&9&_rd;ph{t<#J;b5K-QK*@ zvv`1a$NsY;9;vuKJL2K^9j=G|nn-;u{}*S|v=N2)?KW(!j?P~)IG4W@p`)ekC68mj}|A@73Wq6r)UBMl| z_20XHA;6D#_bY_>cep-E{P#G2iiYlqmDe4E&4R&T0#{%# zh`#hbYtyR{E%aGT?}!cou3YHF^iUkGY%rK!6RMfTruSY#nKdz?nn2(OftdV1=kAwB z{|EcJ?{ntf8BNofk+f1qTheY$n`-AST>qP`cH#|jx^Kk4-#4y9E+<}_Hqfp~8);Xk zEofJzEooPwbu520t*2d)w%zUr}+e`~hdnsfdCdmevb&R8z~O!*vtDhFKRR9o>!xS7ZF*`yEfT^ZBK zFPv(}!nL<;oR8@)A#9moxm5oa5(ls+nd?OJP6$`7pN#7*lz zhxy~P&7S8Oe5UoWdq;d)GPM$)th_sf<5zi)kHe`};-f8ZwGtm;w$G5_L$R$(#N*`x z=W>bnm))V9|FXw>$r;{V&hf5tz&pzk?;sa=Te-wr$?h=Df7#qYJPO;fm*cAP0WT{@JW?+32)V?=Wp@PUzwGf)oN6N; zf~WC%LXPeAgn>5AY9VnyoN6KNgKd8l_x{_)EFK|CCGo57`~XpLi-xwGdx#{W6w| zuQiX0^%7rYeQbHhGM-vE#imW-d64)Lv(-XuweSLqnRiZnF1CBNfG6T;eP|2Xb7)K2 zvuINdJdXQ+v(-R+Do*#0_#~XuHfRIw@wAclSlWX2XxftYNLqJ1_y4q>_E6f4*6#Vo zW4qVSiF2HeAG|-d^D5$ftdHG4;=Qa7>rvv})!&`K`EPxwZyxV#d8=`F2ldJEwsOE* zS^wE>9TAU_3%rH(vHM57srtJH=fCXn`ev(ff%E_OJYLJpe#-Fb%ICNy2Rs_58iz;W zRO4{f^1QZD;$^MBkLmsQk!Hs}ZF2t086Ga@xE-h3hKDL2@epjsbAbn?KGZhcAG-;* z{rlD0aH?&@4%@Oayr8GoIsOHw+J=9^skY%C%vRg*ch<*h8=j9-ZJWsTKTfp`e~wda z!}D;eZFsJk<=Xr2pUC$9`-jP`^#1$%IMp`t?^vGoa2)GzraZL`zlJkft8Fi*JhhGZ z1?xYD`QvBJp63lb$NE@p!`;c$HatstH<{P}mG}5zoN60>$nsX(@cm}1ZTKE+>k{#u za)D>cC7vO>b9wz=_BhHJo+{_~dO2X8+B}CUFK2kNoZ|^{ zz~{&j{|VbUSm4u@FYzg|JD<<=${rstXZRRw$6k()R6gLtU6CkNloIKc{*}ersaeM+F{> zztL+8CGoGke(+Gn{XhBPTx))**B0D`_SypX_q?~iKGW9qd_R@${XNtEWA~gRxc+C_ zV!VI<8TX_1x^2d^5nSh=#x?%ROiQ(r+=@6I^LTkV;^pK5FC&+DDcN1b>wmJxi^&-t zCg*rzoN6T=j63a^!~<~p&3Wz}?S8o+w-?hYW?R?u}tDP28J6|VmR#-mqD>&6o{G#>CST26fJdWClpSC`> zyh|BR?d-N`oco@5mf312w%Ylq#mqY={u}N$j^*Hf={~Yw?S68R_+HwA_HNpe_DUPoKfUQ6qyaQ{#1X|JSB#~GfI zyksrMm*8}K;R~^y#}S`reXN$^N!Ew;DsfZ&-Q`^WTOZCHkI%Hc)lz(#`sDazIpBu% zpWW6G@o{p2kG4KmOYsrv@2;T!%N~z6TP=-j-{13ie>3|n!}}H5D(9Q%%LonXRVcWvq|YRJ;^UHFY}Y zKTb6jFNRZ1#lvu_sd!;C%MEz29Pxl;YAWuFQ%%Jc%Qsn%o4No0#PZZs{436At)~8* z^3+t~A91Rw_-`bFsiTD_`Q4>~7=rKiT6ON9XP@cco1Dz<%5;FD5+u37O3*xhdZ2iJ~G`2jUgd=$=7 zzJKj-oa!I(AvhhM_#ipr1LXqmCzp6{+1L1=7ciQoTx50K# zTwuE=9!qTZ#3gZ-OwGHK`kzeA!yDsN^Y8{KPtC*YV%r}9Tg_X``dQ5*wwkw^O|P=8 zh^^+WY-6qF5pxVQMp=K)Id>Q1sd=6_)jW%-dCS;zt9cev^F~-~HP2#d-r~f~3d<*6 z6sMYp7qNaB%f$Sgo2}+yt9c7PCT89_@h{l!6$1VV zN7^503)+9vmbBl|rdoFo*Z*d#b@(fs?icarIH&!LHqicqHqy?eEoe*HlJ;X-cQ4of zw4U|@+EnB4yZFy+bGzTqiQgn1!M0Dw67j3VOR^8_9E-#+62Hs7NHvf6Sz=qS63;O+ zFLxjN3#XcgXW?tAY!j<{#E%l6!v3?qIq_dLF5m}j-0ZfFi0_pPe3$jH+J|pfe|JCU zzwGhNW~+S#=l}0{{1j!ubNx@e z9><-nhbNvyY|d~~&hgne)j)hEUfi=S=og7kwU}eINn8-0nEFx+@$t#+HTM+P|2Wk^ zd?bE=&yuBO5g$st7{|B8Iq^ZnsTSe`ajJ!QKeN?BytnnST8Q_=sTR)W{Ku&l;&C|D zLc9}BwGeM_W?2DmBS*YtGPMwQ;#3Rq=9X`=U*_=of67w}@rF2~wOY7d%2Nx8JFGu# z;BQT{XItUbtdG?~yh<{)5U-@Xdz#PxDDSbysTSftSl((OUfOK65HE>sT_RpwF7Tpq zi5HRGGo1gj#{=aI_mgwnM-KRZA8G#hzjA^9BbWI9WcMuRzwGh9y z{{!23THqe#OZ>6yp5ygD+2i-*48M(=cKqNsln?k-IpUY(0zWU8_!-$f&+C7(#|2Kc z5I1&5TA-uEyO3KJhc#?kUXLm@Uh8D)*?PCd5Kzq4^QT~2_KTYc+I`c z`JcR4&Eo@cyVX40PR+CH>3zrzvARcY2zAfS(>=&FE1YxW>NwRtybDgX5ASFmNA1Jg z;Z;57^edbb)IPhu-O{E{;=Ci*X}0=@t^RFhG4s!fH^%9n5C4A8w;s7jybf(ayEbh} zyC!X_gRgS_o2?GwRdBlJ!@uA2jUty5FHak2J#D02mbRc>hPI?#n%2F>>j<=-b_v=F zuPJ1>i`Ny}iEW?e#1-!07qRKYfp{To=UBu8ajJv3pY>t=O58{N-RqqHA9Aep=ds{U zYQ?X_sSXnVY_{d#ALW3*$EgP5Z{-4iZGEf;;xE+Sy+Qq#J^s{eHL&Ed{GP`jnKRZA zf1rGh-<1P?3#S^0U&pBi;#Y7hV}7_2V&erH<73_RQ2))2^}}=I40p>po`rkte>iZ} z$Ht?Y7K#6cn~X2;gO=wtpAuJi&F5a5X7_?~8JF?;KXIyo#51ig<1#!$&hd1dY9Ed` z)jmAcY_$(xZ+)!x;cIcKeV_9BA5OIoUyf7l!SF9seO2Yxx#v2 z+g@i|++;oeLH);l_#YgPC-A>FcFkR?8`fpc2>{L^tZb`HxUKHBVAFMNdc zw_1n~O{Ny&@yfe-99PPFygyF05btAotA%(kv(-YpJGOO;cvrc=JIf{BL3W>U|1W#I zm7L)*a*nr<1Kw1Qcq6&M>&qoxM|S_@^?%vp)#VJ=t7I<0ZOFU9` zpL71p9uJo@+>V=Uha3-8KHwp8#DnAl_m@lDCc7`#-?GOG{?2x`|JT63n5`D#pKzeH zTKEG_wUGEbZ2O|X^HYCnA^r-xFMDg@=P5s+=85OwEam&x=HgTfi9f;Vc*P&e5x*}N z_#L^#Z_4f~UjLUpep$}&3v!O1#is)a|CXP`^BFxrC$<{5hfOCA#C2@vPQ<(5RO9fD)`#^e@pkI(zT@?OoN65I zbg?nUrrEt7@fPZr<4xs&H^QmL;q~PLuVa0z#^JTp-+j;dFMC`wTa9zg`sm;Dc$7I~ zxwxu)j+d1K9*I+p!y|C2adnOwfwhNi%p8_O{E*r6+=1`6{#M)YJ;~HI ze5dlRU$Z__d5>q{RNL@$%Uf;3(QLI1PsO%Q5nnGC_*%KdSIMq_vwpek@uhNxFOqY7 zz8vsmIpPU&fzOdk{3qEBXx2}cJw8Rw@QHGckCy{JMvnMMZ0Bcz4^zIx2g`0?v)(0p ze1M$ceQ}fRkmElpAMhS>#C5sAyT~QpQFeox_3dPjx5lZq;jw0`Z8*b$)@s{kIMp`d zjj`>E0&kG|Q`_*m*bTP+gKKN2JhhE@4VXV90xhzvv8_+_zbzgr&=GYcladrckRvk z39`q>nmy00zF9x&_dGsa&hQ~}jt`OpKG4kAi1$;zzr=<8g9^cQOx* z^&D?+=2#AR8@$kN*VZGxXk_HLSg1CJ++cDAxeL#<+~C?~Hiqq8lG|8z%X9wA9 z$7n(BOBt?q#mx|9hJJ-tM%A1<8_07z7+~AGn3~wOk zcwIT*was?i;5C#l@Tzi&SC(BD=fCXn@^XfkGuv^4moeLMgO|eTxWP-{g4T|k#jKAV zH^jqacM$bo_IR+I;Q?}v`{H!m;0jL14gT%jUKjXRY<)`nGfv0Nc+P*EjvM^FoZ)ZP zC&yn~emx#P{=)KoBj2mptbb-6#d8$ysl>)y;?m4<^9fGRMF&&=v8T1;=6#%w8{&7& zCC3eZ)B4+SgI_b-af4sR>A1l!SbsZi@UyZzq*PnAo2J#O(_>Y7~tTi)i6?LOv8 zn>LtQPCP|pa(sy#@P%^3=i#1DZfY;^B+CzEyWmQ{*a*a(wjL8W|24+rGcfmvd@hvw zZ+VuV<4Wb;#)%g9oyK=FasHdBT@fE+W?lt8QZDgf=8|j8iHz@QHO3S7FveT#W8*;e z&4~AtbG)}4@SbwSyWuL2r@&1Y8@mwqR9PqD9o5fG;{3Z$~n$()kQuh z%K4wny5LUcyAknNTOZUEZ1s*Avcm(eG^|5w$F8BYIr+#}};eQPmCGK2#0^i-lcIeCZe-Kw4 zzuiDQ*!uD~10Enp+*dAeMK1Ag@AUR}=W+ha9{((7_(wU%-^&4ii#ypL5w}Wy)04Ow zcwLS7bDOp}pMSvf(ipBk&S%*Z*q#t)ChG1*hL8#6QUa{~$;Fom}Aga*4l^-4&ewvd8n}49}Hw z{D~a!huGFX;`ikOzay9UO>EoRT}l1N|5%Rac)TUYj2%lal54B5FUXBexwhIf*MIlfh60=`-Kh+E1R_%F(r_y*U(l#CFdBRuYFhFiHxma z&)@NKhL4eRe54%kVRFO=%LVRAJ~w^eUbPRu)obyeu1?>tw-0X7$FqO-!cDF-GrYU? z8FpSh$F?oUSTvvtE>&hQ`P94{>gyrh}=M%>AK7t^$Yc$i$`g=N>5&-Kb44>0o>GHj2tEyd^8 zb7H4{0WWx~_i;u1i(KHJ^y@-q%y!<5}is+2{B%<#YV7^>^I= z;!5PV`fOSy)+6zKX&TGG)oQD8m&Km_O?w#B-~H=Au=U>g&yUv)zI zKE0`C&-F6XuD5rXgnCJQqWS0>>W+Jh zN@z8Xv%VFsH;J2hY#c@0=~ymti{&0>{j1b)V*72Y@fLF~1Y$d<4ouUz7P0Z?#m2tG zP0uwKap$Di*o(N@$!kgs-dg#9 z$I20BxRb|H;LY%K?gL8P7VNdKw%Uf)cM*Qai~M>T=W&Oe;WgzPuZC@0z^hoEu@SGN zJ_TMuF0q&0!kquu#(KQ8<&Wi>9WSXqIbK{2cu_gxMexKL>w(*H`yJ%A+8}a0L)d5J z`m3*7gz2)!PR{UxH&u)9FJ{ID{1diq8u1TufxnYWJm31WOgEJC-+VN`b%sAzpA65F zb39iL_!B&yV>#ls%xZI6?E`Y%gD2Dra__2diQkePuYJ5Od;AKvaT$IQ+t?gGr#=Bc zEl2#6T;M0MjV(}Szdc+5smuDGxKQqfK@!qKq$LMfA*Q>n8yP18gXLua$;`uqp zJ&yWM+~PSZ5O0^Jaa`fbBC)ZRjpMn$ARc4pdAhV|8&dx*UWj{~C8+=AWjHtShPcJ! z$?$r(iF4e6J8{5kT7Tvh@oKnhNtS_Eu{`rB@k-d9iyhNeusq*U$B+v%jPoyT`YSZaf|7m*y=(Lab-N))~35w<0I=|VP6tAo3ZgeaaYdr ziQmEYxFdek>{w^~8m{0Hzijp_Z)v+ux~=iN#aw55;x5Pg`ot6In-NbZPUj7|Cb^v4 z6SxZp++_MA#4X~e_@OjC)(hhMaeB_c_h6fc8_ECm;mT{&LOfH>@C-S})8&Ap9Pv~$ z^C|H4xN9uyg|EeJ>{G|FG+vMU3UZ!!3T;MvDQ!-BF>Ro|kk;lE@pU}z4rLOa)$pS=lK8Rfd4H={4cq{-{7i?={r!rRNk$?=lacIBJW%B zxqjST;kgd?;7%Sxjz3oafd7uGdz=X*RQ@!fL3cgPXnCKvb?xx~|C$F;*vvd1^# z7VDm2+i!m+?zU}2Y|pV*DGtP!+c>U?Bd+v|jZ28_b?icYFHl?(pKES@8YpB z!Qu{{i;0`fR^x2N8S$BBTP8kD4)|m_;)Yz{<8YJyB|cht$Fh!)JwDX>^kKd5cwDV8 zf1ImN!24StTOYg+ZuMhb@LuXu;@zze%W|vmxnAWx-dWD@4%UZd<#=0c%L;fa^@(_l z^0k@hEJ| za;tJ)tB=RaS|65`;gQPcc!V7AaO=aeB5udFtO5^JpArwTJ}k?v#^-vmEz9Hn>XYF% zImb>8c)@GE`#s`caJ4`4$3Mv>{y}!DbNw%SJYUZ6S8|R&mjj+BM?6<9@F#MKKa|}X z-2clSzk@5beej!dj$e}lep!zA1-Zb_$|as7yESNtl z0-qz7_)oHZFY4*CeJ|=Ma)wWob9}rU@G)}4N6H00OfK=kvRjAeH`(I@Xpa)wux zbG)(~@QQN8%gY5`PA>5>vRj}3>z6%VLeB7Fa*l_|0WT~^JXkL90J+3{Ww!x!K=$~z zS9N~jU*#PCEC>9f9P#&ZfxneY{I%>h3Ml_$}FO z#QneQ@hftMUzBtFoE-4ea>P%`1%6U4@#C`FnEQWoU!FhlU(G(&GyDLqx>(Qgz3LzE zU2?>?r~W*DV!Q8}p=l-YblGjf`7e7sRnGABa*nT+1HMX*_;R_xm&zr+NOqfY{>vUu zmNPs-&ha^Nz<-h>K3y*GDRPNVl-*{W|FXx&$QeG;oUx7YVP;Q_#8t=p`ovY9%Oi2F zF$LaVF7ZCH+no9@d%U}x;a%k%?<@zrgBwwo! z`Kk4QySOgvAZ`v}9f;S|m;$dRmv|M~W!(Rpd2QF@6>zK1#PnU#cAvW(aTnVoXWB9v z6Yx@U#7oEpUQ90WFxho-{>vT@mNPs+&T(Hk;EEjaZ?Ew99LFpERW9+*vKzzsk2_sV z-zWV&Zu2~Ul5ZQ>dms4?xo)mca&nv>jW290k1-H)?lk6EJdkZ>aR+0Fn-!Kx%zPUk z5trERt3R;5&vAV+mcG0{^A2&BEt~jF8`pj5wtly0dTG-~#74{Fz&o;%C6?k9YrZrpX`=l?xwk=Agoh!Sox$l=f4swRi!fcy*j?YMD zI<|E?h4@_N6^Z-vUY&iG>I8EA*q;TtempMwT-4Fz(lW^%A-iq3{>P&j@9`w&XYa9g zk!!R4NbdJzXg?dn`sC#H*7$(;#5|6A#Jgczeu2l~lGc{Jllr@Dx&N0v-X`_uvjccb zoYUGd)@glMRv_M7{UhE4+p-G0q4FhO&+_zl+w~qNYZG@7d*U_JFT<dk|Czp5`*=^7HFMGU%oZ-df91oKNURaKJuw38)a*6xOZU>GR+2h|{?%i)0{#DNL z&vL*&$`OAr7x-Jb#9zy9N9w=q@n>>|Kb3P_$^m~QNBn_Y;CJN`za_h!sQc8yqU*!xxAm{jAIpDkGh;Nq*e5+jIn`O5PuYt-Q z|3%L54RVhEEC+nG9Pt%$fiIIwe6j4t@x4CSp8q^P*YX^z8MgCz0()1mjZ=v`L#P+TP1kCiXwzHlJK{=ZdgC~YE4)6rE7QAp|Btv@;k+R3 zAwG<_i#Q{$9K`o}6E~~8&P&|wTa5#4dKa%{5$}iXaYk&*{v&aFi<@HTRex0D0!lq23;F7PIDi8qwpZk+$J#~pHp*OYU-8cy31 zucCa!D<#irFYpR@e0fv7#Gfu_&xLKZWo`Ozd_Hh@?*DO*Z9hCBjbq#4;mNbwbKGuy z7#HwR%QH6OA(m%sfd{4jY%AP9c~-mIgXdc7!&r}<X}p4`U;qV|m6FxZCoKE%B_> zKh)hHx&Kd|)$Z}b)`zhfe#r8S&GG$~XKcXtr2ag9d}lKI8PBvnj4kmD%QM#P&Go4zC88k@#9OA*}wa6{cn93>+$)PXKaQi zTb{8wo{;+U`0+W(>|gvR>%-UrpKf`^miQFQGuG|P^?&NmK_|0lD5ab|rOo8iqY&)6JqY5)*PH=Y^4M6GxJn!?o-@6%G;xK`4j05rnweJO z#btLO@BhmlFCu4nA@du2Ckh^DUiq?m!2Pf-Gva0$8x`WNz`77uw!EdW;6?f_!RN7v ze=*b7<(&Vr$3Ms!{!Y&EeDnAl>H&Xc{_w_n#8sZ7|4E$c2XXgP(;A-=_hh_|Pt5&Q z;}h%eSXLM3znN(sSF+f6kGP9z8F9cumfy|iSTk}Dzxe-u{_Q#Xb)Wd>y615< z-+HJqhq&`~-t#7|OpJ{*&*LYg-=VGWm>*|)stHVg1gHB(Y}@7`Vq5ouzx(A9-y^#U z&ndFUGvy4=z_wmFo{oDM7jU#bKBnK1ooe|lMsyV9ueUsXN_?&Cxc&89qtQ@d=a*216T|cfVWRJIzGrXmo<4!r?&E<$U zkqf+`T;la)*Pm+&+2b|k46lZ(o_&BQV{{0yEi)1iQojQCmrL9xyMdg?>hJM_7ns*@ z&R_fsZVhHV@K5F?SO)&V`V35^AeQd8C6!;y>|HAz7o93M71eX0;Z~ydpwwL9M{0n9u)92Wp#kNlZ zdD}*_)i2s(w`nZ9z)x77`Ih)mGh^IByr*bhK#ju>n(3e6`{W$oEeCuDuJXJTadS+o zaVv3Wm1S6*#m02&+lSBo5O?x;ZnFM?Wes6E@eRZ+mhFk{+`ZQN7o6vcueAO=hMahc zP2>7B;GUa!O@VkU(<1Q-%*&p0&$B+^-1M8VlgvynnckG$!hEL4%=Ys5Oyx6tnw;a4 z<$xP<#K++(V+(w=<#CCRz@3hJjYW7(0ryNC+Tro|WY>}59NRIG9?sAEDmEBNYQ10EwsyoFrgP301AB)eg}rYL*7j-27O=xxcMcLz#a)w99IUbHVKGNsc+HuvLmp-@F(|<-|5#sJG*~j$j zI(J56FmdY{t`CR@Sl^7}tG)N0vCXCx9ACseuGRS8^GxsL__FEER^wOVRyX&2HoY>f z@smwop7V#eQsKKoh;4sF;(sM`?PJq8R=?J?lK2bUIh^x!FJxB><@vssh}X3Kc6{Pa&Vf~}ALC16p4%FuiJPADZzShPjExo4-xGJ!e>v-4 zvOHqzKhk36mlKbW10IgkH8XBkzQ9A}5)YBxGSq+BltptO`AV<>J#vS=WIP< z`W)IX*d9Yc{wKM_KVTc{{y_a05F7upY0I)-h&y?1_}ZrP+?EmBHP7cZozI)*7Q1PU ze^|_CPJ`moV*B4V;*ZR>O#A_E>%-^Baa(!~yRG)NraPAR2DanJ<5!i>@JqN#&B?Ld zuRKTGtWN&J|Nv2|RIdkS+m>xUo0l`x^6;cnLT zK8puXyNK_`J&XzX4&358CF0wx54Ebmx0s*exWLoQ8?p?~XNt^xp4H-wB4Rt10)JJGohigEu92d}+y`B3G1tNcG5fo5KCvCACGlijaV)dSYYLdx z57Xz@&ar%pWs}Aure~1>dPPdY<3^C197E;<*(4Y4Ynb+&&_(`Eo~ZOGu#@&eIs#;YoT0yHzlqzHV{{N z-)lqS&e;>w=i54GPj9TNxUlIwmbI*Z!9FGK$y$xoh+Ew(dsJ_{vc*;Iku7H1j5T`7D|hpn0`a}p zk9(dBcQc}j#f%TcdtnE%33p~J4|lS>r}+KPPASX>)mJJ;zVsPrtsn9`L7M z_x>-?6XYWCW3&bBBeW&$-)PxW^@nIZ?E|zK?R~U4?LD-C_Ac5;dk1YnJCnAgy_MFj z!}(9^X{Xa>v@P14_9oguJC!!l-auQ>UPoKfUQ6rN<@%r2(_TrN(OyoQ(_Tg!XfL6S zv=`A9v=`8pwCB;f^{D@}o^~Q_M%$##Y0sezv}e&q+B0bj+S6%E+EZ!W`rQB1dfF3d zGujhqbK2u*1MM-ik@hIsg7yg7lJ+oKw*lAxw4Qc6ZARNgo6{ah8))~ZjkNpH7PNcQ zmb81(I%SlW!XlQyT_ zf;P}@MjL52p)F`PqAh7RpmiH_{ZH#@*P+d5*QV{^7|L-E*AlA}r*ne1CPzG4F7PP1 z#8ugC!u`MO@klwtBjg+pmjiB>BOWRjc!*r$L9*MF>wnqfHaWvi&hdh$_4x5Ga>PH$ z1^z)U@prP@jMx8UkH3;L{JEUtd2+yW<%mC#3;dy6;`e2@Ij{f89=|DP_%%7lFUtYH zAV>VHT;Mr!iMwUD1@9@!9zP~$_+dH656J=FFGqZjT;Mz963>(!&-*iEkEhESj&hEt z$^l<5M|`bZ;H%^kUoN{&-cyu4zDUmS`ErgY%K=Z2BR)qi@So%opDw#Gyr(F8e4?D; z9Pwdtfe)5T+$FoQ)PLFIedP@QQO@xma=>*t;$7qd?kix&ZcVR?c3R(fVfwt-=H~38^m(yO@T39MHu~O7y|>Rlt#8vE`-|K<*zP?G zyq4veM~PQAGY_{F=YQ(cx90I^%QH5^qf(ytU2)a&j172MGh-tjnfmmt6?lZ@8C&Av zDbM?^TXX(fp0OSeH8VEDLsB2U69W&jJYxgypYpu#irXyD*aABYY~5CdBzs_^OWa(bv)1VjCI>`{+k)=@h7QI z?`OU~v^-;T^6#fS@2lf?EYH}8-!wC}z^~!v==16&?mX<)#!D98dS3e6S2y2(^PKfv zng8G1j_HTr)|g|{Z#%!95qE!nTcfb)t4yxvHvN!W8jsubTPD|oO`ms5<6)b=68~#& z(+{1|c+jTL;B{7;{@IMiy*7O`-*;@&kC@rGleqQt?plrsv%M+w{nBXVXjmr^u#Px&F23U9H9x zo8IEy!KU}L8W$6HIri%g)b|Sa|JI+_6HmrfoY^!N8%<5iiO-e;K2whPG`YYh%O!5e zZb$0B?D5fZhL4bQe5f4ocsb%+F7WK>uFp~=6V`0i|zXi0{;E`43;97zLS955@z;YfftiYJWO`uxcpC<@#Ut_cIp8aCnm?W*7x)sK=8rE_-sRl??~l{`@jlAi=f3vBY5U;at&eRVyem%g z$2-db?|{?%@wRe-x58=uc#QIHJoO)^?SnT}KEoT~G=IFl9Pm2o6Y*Mdfmg?A{9)AqrmaN0h&YPRizmz4t^iPPi9Bjf@P$7%k!U3qs1*Z(+eA3VhR*!IDLaGF2v zF9+O))BLfM3%sCP^T)qn>*EgP`X8t5gMUyy!{6aFe>`6f_$&2^_;b0y^Kg3nc&_s9 zFs}b`+CKP0oVE{s-`t<=gWr(@eiNt1k6)7u{4!4S$1f=F4(I+Kr|pC1Sf74uAKZ=8 z{P8R~;Ky*9KYmy)@IyGwAK$OMJA(R;)Aqr4Dxcw*IL#l=kOQ8sJ`qQ`z*BLWKfYdh zcO>;6r|pBU!fE^9%gwfZ@TGFV7vc2y@%eIrC*w4KJVAMP6!-r)Z6Ew6>tovopN`Y~ z@hNh^C*m}Je7s!XV{n>3K2mviH242FZ6AEF@)_>JY5w>CIpBTOC*nWK1>OUv`Qy6s z?ilX>aoRq3N1V0~-p*{>2X8G0JQkwtg7>G9*AN$i;IFNZZ6Evv zPV>i~$pL?g)BJHM7x*Kb=8r#6-ZgmrAE)hu-%>uquj4d-{E8g#i|WI>TlMGU0zZw@ z{P9!DyA%2R4^G<$KaSJ(!H<}2`{2LI0Y8A#iSDDO_;`X8t5gHOU~``{DIwteuia==I7^!V}Na)A%QY5w>i<=v@V|KqfM@P5|E zwh!JLr}^VO<$!m?Y5sVeT;QE>nm^uNd3PGu|2S4}Kn}`QvBgfM?@0e_Y4~egdcY<42WuXLJ3Jn`{%0A5=cW_u({ue779%9qJSD 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 z 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 zkN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg z011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!) z36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^q%=O?hV(vVE*|J>ZZV5oor2j74G z+%tzHKmsH{0wh2JBtQZr@c$NpEH%As?EhO?W^PG<1T+CtEv?kke0TnTWQ5?vbUgD9on8j=uXE*Q zE#3czPT@axd{al%f3JkE4*p@}_M!cx8tKVCRI=~fApg`CzVl2$>w!#l<+hBk&Hw(` z6;~I0EDybBg$WDUwc3;^5btAAUw6%)i;4L9T>A;veAjh<-!)IU?q^-|9LzE~ zUd{M+(PE?*5EJ$!m?CegYo@torfX)oX0~gZVj`NUFiQ|_x|p!faLoeOoCC8A?!{ul z-vZaPU@nEd(zV|Qa|!H0*Q|vp@~m;qde>}r&8Ta(iHZ0(xb{a~`*zpd>6%}2%_qb} zyq&K7>#q5=2G}u;+kgqw~N$9 z)MMAgE&h=J36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg z011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!) z36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@ zkN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg z011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!) z36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@ zkN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg z011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!) z36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@ zkN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg z011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!) z36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@ zkN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg z011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!) z36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@ zkN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg z011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!) z36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@ zkN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg z011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!) z36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@ zkN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg z011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!) z36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@ zkN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg z011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!) z36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@ zkN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg z011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!) z36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@ zkN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg z011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!) z36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@ zkN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg z011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!) z36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@ zkN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg z011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!) z36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@ zkN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg z011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!) z36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@ zkN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg z011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!) z36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@ zkN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg 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 z 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 z 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

67e<*hUVSM2(jyH#UzG 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�SIFk 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 z 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