diff --git a/sys/arm64/conf/GENERIC b/sys/arm64/conf/GENERIC index ceceb0e19c2e..9470c04646f0 100644 --- a/sys/arm64/conf/GENERIC +++ b/sys/arm64/conf/GENERIC @@ -1,386 +1,387 @@ # # GENERIC -- Generic kernel configuration file for FreeBSD/arm64 # # For more information on this file, please read the config(5) manual page, # and/or the handbook section on Kernel Configuration Files: # # https://www.FreeBSD.org/doc/en_US.ISO8859-1/books/handbook/kernelconfig-config.html # # The handbook is also available locally in /usr/share/doc/handbook # if you've installed the doc distribution, otherwise always see the # FreeBSD World Wide Web server (https://www.FreeBSD.org/) for the # latest information. # # An exhaustive list of options and more detailed explanations of the # device lines is also present in the ../../conf/NOTES and NOTES files. # If you are in doubt as to the purpose or necessity of a line, check first # in NOTES. # # $FreeBSD$ cpu ARM64 ident GENERIC makeoptions DEBUG=-g # Build kernel with gdb(1) debug symbols makeoptions WITH_CTF=1 # Run ctfconvert(1) for DTrace support options SCHED_ULE # ULE scheduler options NUMA # Non-Uniform Memory Architecture support options PREEMPTION # Enable kernel thread preemption options VIMAGE # Subsystem virtualization, e.g. VNET options INET # InterNETworking options INET6 # IPv6 communications protocols options IPSEC_SUPPORT # Allow kldload of ipsec and tcpmd5 options ROUTE_MPATH # Multipath routing support options FIB_ALGO # Modular fib lookups options TCP_OFFLOAD # TCP offload options TCP_HHOOK # hhook(9) framework for TCP options TCP_RFC7413 # TCP Fast Open options SCTP_SUPPORT # Allow kldload of SCTP options FFS # Berkeley Fast Filesystem options SOFTUPDATES # Enable FFS soft updates support options UFS_ACL # Support for access control lists options UFS_DIRHASH # Improve performance on big directories options UFS_GJOURNAL # Enable gjournal-based UFS journaling options QUOTA # Enable disk quotas for UFS options MD_ROOT # MD is a potential root device options NFSCL # Network Filesystem Client options NFSD # Network Filesystem Server options NFSLOCKD # Network Lock Manager options NFS_ROOT # NFS usable as /, requires NFSCL options MSDOSFS # MSDOS Filesystem options CD9660 # ISO 9660 Filesystem options PROCFS # Process filesystem (requires PSEUDOFS) options PSEUDOFS # Pseudo-filesystem framework options TMPFS # Efficient memory filesystem options GEOM_RAID # Soft RAID functionality. options GEOM_LABEL # Provides labelization options EFIRT # EFI Runtime Services support options COMPAT_FREEBSD32 # Compatible with FreeBSD/arm options COMPAT_FREEBSD11 # Compatible with FreeBSD11 options COMPAT_FREEBSD12 # Compatible with FreeBSD12 options SCSI_DELAY=5000 # Delay (in ms) before probing SCSI options KTRACE # ktrace(1) support options STACK # stack(9) support options SYSVSHM # SYSV-style shared memory options SYSVMSG # SYSV-style message queues options SYSVSEM # SYSV-style semaphores options _KPOSIX_PRIORITY_SCHEDULING # POSIX P1003_1B real-time extensions options PRINTF_BUFR_SIZE=128 # Prevent printf output being interspersed. options KBD_INSTALL_CDEV # install a CDEV entry in /dev options HWPMC_HOOKS # Necessary kernel hooks for hwpmc(4) options AUDIT # Security event auditing options CAPABILITY_MODE # Capsicum capability mode options CAPABILITIES # Capsicum capabilities options MAC # TrustedBSD MAC Framework options KDTRACE_FRAME # Ensure frames are compiled in options KDTRACE_HOOKS # Kernel DTrace hooks options DDB_CTF # Kernel ELF linker loads CTF data options VFP # Floating-point support options RACCT # Resource accounting framework options RACCT_DEFAULT_TO_DISABLED # Set kern.racct.enable=0 by default options RCTL # Resource limits options INTRNG options LINUX_BOOT_ABI # Boot using booti command from U-Boot options PERTHREAD_SSP # Per-thread SSP canary # Debugging support. Always need this: options KDB # Enable kernel debugger support. options KDB_TRACE # Print a stack trace for a panic. # Kernel Sanitizers #options COVERAGE # Generic kernel coverage. Used by KCOV #options KCOV # Kernel Coverage Sanitizer # Warning: KUBSAN can result in a kernel too large for loader to load #options KUBSAN # Kernel Undefined Behavior Sanitizer #options KCSAN # Kernel Concurrency Sanitizer # Kernel dump features. options EKCD # Support for encrypted kernel dumps options GZIO # gzip-compressed kernel and user dumps options ZSTDIO # zstd-compressed kernel and user dumps options DEBUGNET # debugnet networking options NETDUMP # netdump(4) client support # Make an SMP-capable kernel by default options SMP # Symmetric MultiProcessor Kernel # SoC support options SOC_ALLWINNER_A64 options SOC_ALLWINNER_H5 options SOC_ALLWINNER_H6 options SOC_CAVM_THUNDERX options SOC_FREESCALE_IMX8 options SOC_HISI_HI6220 options SOC_INTEL_STRATIX10 options SOC_BRCM_BCM2837 options SOC_BRCM_BCM2838 options SOC_BRCM_NS2 options SOC_MARVELL_8K options SOC_NVIDIA_TEGRA210 options SOC_NXP_LS options SOC_ROCKCHIP_RK3328 options SOC_ROCKCHIP_RK3399 options SOC_XILINX_ZYNQ options PPS_SYNC # Timer drivers device a10_timer # Annapurna Alpine drivers device al_ccu # Alpine Cache Coherency Unit device al_nb_service # Alpine North Bridge Service device al_iofic # I/O Fabric Interrupt Controller device al_serdes # Serializer/Deserializer device al_udma # Universal DMA # Qualcomm Snapdragon drivers device qcom_gcc # Global Clock Controller # CPU frequency control device cpufreq # Bus drivers device pci device pci_n1sdp # ARM Neoverse N1 SDP PCI device al_pci # Annapurna Alpine PCI-E options PCI_HP # PCI-Express native HotPlug options PCI_IOV # PCI SR-IOV support # Block devices device ahci device scbus device da device pvscsi # VMware PVSCSI # ATA/SCSI peripherals device cd # CD device pass # Passthrough device (direct ATA/SCSI access) # NVM Express (NVMe) support device nvme # base NVMe driver options NVME_USE_NVD=0 # prefer the cam(4) based nda(4) driver device nvd # expose NVMe namespaces as disks, depends on nvme # DMA controller device a31_dmac # GPIO / PINCTRL device a37x0_gpio # Marvell Armada 37x0 GPIO controller device aw_gpio # Allwinner GPIO controller device dwgpio # Synopsys DesignWare APB GPIO Controller device gpio device gpioled device fdt_pinctrl device gpioregulator device ls1046_gpio # LS1046A GPIO controller device mv_gpio # Marvell GPIO controller device mvebu_pinctrl # Marvell Pinmux Controller device pl061 # Arm PL061 GPIO controller device rk_gpio # RockChip GPIO Controller device rk_pinctrl # RockChip Pinmux Controller # I2C device a37x0_iic # Armada 37x0 I2C controller device aw_rsb # Allwinner Reduced Serial Bus device bcm2835_bsc # Broadcom BCM283x I2C bus device iicbus device iicmux device iic device icee # Generic IIC eeprom device twsi # Allwinner I2C controller device pca954x # NPX I2C bus multiplexer device pcf8563 # NXP Real-time clock/calendar device rk_i2c # RockChip I2C controller device syr827 # Silergy SYR827 PMIC device sy8106a # SY8106A Buck Regulator device vf_i2c # Freescale Vybrid I2C controller device fsliic # Freescale iMX I2C controller # Clock and reset controllers device aw_ccu # Allwinner clock controller # Interrupt controllers device aw_nmi # Allwinner NMI support device aw_r_intc # Allwinner RINTC support device mv_cp110_icu # Marvell CP110 ICU device mv_ap806_gicp # Marvell AP806 GICP device mv_ap806_sei # Marvell AP806 SEI # Real-time clock support device aw_rtc # Allwinner Real-time Clock device mv_rtc # Marvell Real-time Clock # Crypto accelerators device armv8crypto # ARMv8 OpenCrypto module device safexcel # Inside Secure EIP-97 # Watchdog controllers device aw_wdog # Allwinner Watchdog # Power management controllers device axp81x # X-Powers AXP81x PMIC device rk805 # RockChip RK805 PMIC # EFUSE device aw_sid # Allwinner Secure ID EFUSE # Thermal sensors device aw_thermal # Allwinner Thermal Sensor Controller device mv_thermal # Marvell Thermal Sensor Controller # SPI device spibus device a37x0_spi # Marvell Armada 37x0 SPI Controller device bcm2835_spi # Broadcom BCM283x SPI bus device rk_spi # RockChip SPI controller # PWM device pwm device aw_pwm device rk_pwm # Console device vt device kbdmux device vt_efifb # EVDEV support device evdev # input event device support options EVDEV_SUPPORT # evdev support in legacy drivers device uinput # install /dev/uinput cdev device aw_cir # Serial (COM) ports device uart # Generic UART driver device uart_imx # iMX8 UART device uart_msm # Qualcomm MSM UART driver device uart_mu # RPI3 aux port device uart_mvebu # Armada 3700 UART driver device uart_ns8250 # ns8250-type UART driver device uart_snps device pl011 # PCI/PCI-X/PCIe Ethernet NICs that use iflib infrastructure device iflib device em # Intel PRO/1000 Gigabit Ethernet Family device ix # Intel 10Gb Ethernet Family device vmx # VMware VMXNET3 Ethernet # Ethernet NICs device mdio device mii device miibus # MII bus support device al_eth # Annapurna Alpine Ethernet NIC device awg # Allwinner EMAC Gigabit Ethernet device axa # AMD Opteron A1100 integrated NIC device cgem # Cadence GEM Gigabit Ethernet device device dwc_rk # Rockchip Designware device dwc_socfpga # Altera SOCFPGA Ethernet MAC device ffec # iMX FFEC device genet # Broadcom on RPi4 device msk # Marvell/SysKonnect Yukon II Gigabit Ethernet device neta # Marvell Armada 370/38x/XP/3700 NIC device re # RealTek 8139C+/8169/8169S/8110S device smc # SMSC LAN91C111 device vnic # Cavium ThunderX NIC +device dpaa2 # Data Path Acceleration Architecture (2nd Gen) # Etherswitch devices device etherswitch # Enable etherswitch support device miiproxy # Required for etherswitch device e6000sw # Marvell mv88e6085 based switches # Pseudo devices. device crypto # core crypto support device loop # Network loopback device ether # Ethernet support device vlan # 802.1Q VLAN support device tuntap # Packet tunnel. device md # Memory "disks" device gif # IPv6 and IPv4 tunneling device firmware # firmware assist module # EXT_RESOURCES pseudo devices options EXT_RESOURCES device clk device phy device hwreset device nvmem device regulator device syscon device aw_syscon # IO Domains device rk_iodomain # The `bpf' device enables the Berkeley Packet Filter. # Be aware of the administrative consequences of enabling this! # Note that 'bpf' is required for DHCP. device bpf # Berkeley packet filter # USB support options USB_DEBUG # enable debug msgs options USB_HOST_ALIGN=64 # Align usb buffers to cache line size. device aw_usbphy # Allwinner USB PHY device rk_usb2phy # Rockchip USB2PHY device rk_typec_phy # Rockchip TypeC PHY device dwcotg # DWC OTG controller device musb # Mentor Graphics USB OTG controller device ohci # OHCI USB interface device uhci # UHCI USB interface device ehci # EHCI USB interface (USB 2.0) device ehci_mv # Marvell EHCI USB interface device xhci # XHCI USB interface (USB 3.0) device dwc3 # Synopsys DWC controller device aw_dwc3 # Allwinner DWC3 controller device rk_dwc3 # Rockchip DWC3 controller device usb # USB Bus (required) device ukbd # Keyboard device umass # Disks/Mass storage - Requires scbus and da device tegra210_xusb_fw # Tegra XUSB firmware # USB ethernet support device muge device smcphy device smsc device ure # Sound support device sound device a10_codec # MMC/SD/SDIO Card slot support device sdhci device sdhci_xenon # Marvell Xenon SD/MMC controller device aw_mmc # Allwinner SD/MMC controller device mmc # mmc/sd bus device mmcsd # mmc/sd flash cards device dwmmc device dwmmc_altera device dwmmc_hisi device rk_dwmmc device rk_emmcphy # VirtIO support device virtio # Generic VirtIO bus (required) device virtio_pci # VirtIO PCI device device virtio_mmio # VirtIO Memory Mapped IO device device virtio_blk # VirtIO Block device device virtio_scsi # VirtIO SCSI device device vtnet # VirtIO Ethernet device # Chip-specific errata options THUNDERX_PASS_1_1_ERRATA options FDT device acpi # DTBs makeoptions MODULES_EXTRA="dtb/allwinner dtb/freescale dtb/imx8 dtb/nvidia dtb/mv dtb/rockchip dtb/rpi" # HID support options HID_DEBUG # enable debug msgs device hid # Generic HID support diff --git a/sys/conf/files.arm64 b/sys/conf/files.arm64 index 8c60edc29448..a1f2fe03b24e 100644 --- a/sys/conf/files.arm64 +++ b/sys/conf/files.arm64 @@ -1,583 +1,602 @@ # $FreeBSD$ ## ## Kernel ## kern/msi_if.m optional intrng kern/pic_if.m optional intrng kern/subr_devmap.c standard kern/subr_intr.c optional intrng kern/subr_physmem.c standard libkern/bcmp.c standard libkern/strlen.c standard libkern/arm64/crc32c_armv8.S standard arm/arm/generic_timer.c standard arm/arm/gic.c standard arm/arm/gic_acpi.c optional acpi arm/arm/gic_fdt.c optional fdt arm/arm/pmu.c standard arm/arm/pmu_fdt.c optional fdt arm64/acpica/acpi_iort.c optional acpi arm64/acpica/acpi_machdep.c optional acpi arm64/acpica/OsdEnvironment.c optional acpi arm64/acpica/acpi_wakeup.c optional acpi arm64/acpica/pci_cfgreg.c optional acpi pci arm64/arm64/autoconf.c standard arm64/arm64/bus_machdep.c standard arm64/arm64/bus_space_asm.S standard arm64/arm64/busdma_bounce.c standard arm64/arm64/busdma_machdep.c standard arm64/arm64/bzero.S standard arm64/arm64/clock.c standard arm64/arm64/copyinout.S standard arm64/arm64/cpu_errata.c standard arm64/arm64/cpufunc_asm.S standard arm64/arm64/db_disasm.c optional ddb arm64/arm64/db_interface.c optional ddb arm64/arm64/db_trace.c optional ddb arm64/arm64/debug_monitor.c standard arm64/arm64/disassem.c optional ddb arm64/arm64/dump_machdep.c standard arm64/arm64/efirt_machdep.c optional efirt arm64/arm64/elf32_machdep.c optional compat_freebsd32 arm64/arm64/elf_machdep.c standard arm64/arm64/exception.S standard arm64/arm64/exec_machdep.c standard arm64/arm64/freebsd32_machdep.c optional compat_freebsd32 arm64/arm64/gdb_machdep.c optional gdb arm64/arm64/gicv3_its.c optional intrng fdt arm64/arm64/gic_v3.c standard arm64/arm64/gic_v3_acpi.c optional acpi arm64/arm64/gic_v3_fdt.c optional fdt arm64/arm64/identcpu.c standard arm64/arm64/locore.S standard no-obj arm64/arm64/machdep.c standard arm64/arm64/machdep_boot.c standard arm64/arm64/mem.c standard arm64/arm64/memcmp.S standard arm64/arm64/memcpy.S standard arm64/arm64/memset.S standard arm64/arm64/minidump_machdep.c standard arm64/arm64/mp_machdep.c optional smp arm64/arm64/nexus.c standard arm64/arm64/ofw_machdep.c optional fdt arm64/arm64/pmap.c standard arm64/arm64/ptrace_machdep.c standard arm64/arm64/sigtramp.S standard arm64/arm64/stack_machdep.c optional ddb | stack arm64/arm64/support_ifunc.c standard arm64/arm64/support.S standard arm64/arm64/swtch.S standard arm64/arm64/sys_machdep.c standard arm64/arm64/trap.c standard arm64/arm64/uio_machdep.c standard arm64/arm64/uma_machdep.c standard arm64/arm64/undefined.c standard arm64/arm64/unwind.c optional ddb | kdtrace_hooks | stack arm64/arm64/vfp.c standard arm64/arm64/vm_machdep.c standard arm64/coresight/coresight.c standard arm64/coresight/coresight_acpi.c optional acpi arm64/coresight/coresight_fdt.c optional fdt arm64/coresight/coresight_if.m standard arm64/coresight/coresight_cmd.c standard arm64/coresight/coresight_cpu_debug.c optional fdt arm64/coresight/coresight_etm4x.c standard arm64/coresight/coresight_etm4x_acpi.c optional acpi arm64/coresight/coresight_etm4x_fdt.c optional fdt arm64/coresight/coresight_funnel.c standard arm64/coresight/coresight_funnel_acpi.c optional acpi arm64/coresight/coresight_funnel_fdt.c optional fdt arm64/coresight/coresight_replicator.c standard arm64/coresight/coresight_replicator_acpi.c optional acpi arm64/coresight/coresight_replicator_fdt.c optional fdt arm64/coresight/coresight_tmc.c standard arm64/coresight/coresight_tmc_acpi.c optional acpi arm64/coresight/coresight_tmc_fdt.c optional fdt arm64/iommu/iommu.c optional iommu arm64/iommu/iommu_if.m optional iommu arm64/iommu/smmu.c optional iommu arm64/iommu/smmu_acpi.c optional acpi iommu arm64/iommu/smmu_quirks.c optional iommu dev/iommu/busdma_iommu.c optional iommu dev/iommu/iommu_gas.c optional iommu crypto/armv8/armv8_crypto.c optional armv8crypto armv8_crypto_wrap.o optional armv8crypto \ dependency "$S/crypto/armv8/armv8_crypto_wrap.c" \ compile-with "${CC} -c ${CFLAGS:C/^-O2$/-O3/:N-nostdinc:N-mgeneral-regs-only} -I$S/crypto/armv8/ ${WERROR} ${NO_WCAST_QUAL} ${PROF} -march=armv8-a+crypto ${.IMPSRC}" \ no-implicit-rule \ clean "armv8_crypto_wrap.o" aesv8-armx.o optional armv8crypto \ dependency "$S/crypto/openssl/aarch64/aesv8-armx.S" \ compile-with "${CC} -c ${CFLAGS:C/^-O2$/-O3/:N-nostdinc:N-mgeneral-regs-only} -I$S/crypto/armv8/ ${WERROR} ${NO_WCAST_QUAL} ${PROF} -march=armv8-a+crypto ${.IMPSRC}" \ no-implicit-rule \ clean "aesv8-armx.o" ghashv8-armx.o optional armv8crypto \ dependency "$S/crypto/openssl/aarch64/ghashv8-armx.S" \ compile-with "${CC} -c ${CFLAGS:C/^-O2$/-O3/:N-nostdinc:N-mgeneral-regs-only} -I$S/crypto/armv8/ ${WERROR} ${NO_WCAST_QUAL} ${PROF} -march=armv8-a+crypto ${.IMPSRC}" \ no-implicit-rule \ clean "ghashv8-armx.o" crypto/des/des_enc.c optional netsmb crypto/openssl/ossl_aarch64.c optional ossl crypto/openssl/aarch64/chacha-armv8.S optional ossl \ compile-with "${CC} -c ${CFLAGS:N-mgeneral-regs-only} ${WERROR} ${PROF} ${.IMPSRC}" crypto/openssl/aarch64/poly1305-armv8.S optional ossl \ compile-with "${CC} -c ${CFLAGS:N-mgeneral-regs-only} ${WERROR} ${PROF} ${.IMPSRC}" crypto/openssl/aarch64/sha1-armv8.S optional ossl \ compile-with "${CC} -c ${CFLAGS:N-mgeneral-regs-only} ${WERROR} ${PROF} ${.IMPSRC}" crypto/openssl/aarch64/sha256-armv8.S optional ossl \ compile-with "${CC} -c ${CFLAGS:N-mgeneral-regs-only} ${WERROR} ${PROF} ${.IMPSRC}" crypto/openssl/aarch64/sha512-armv8.S optional ossl \ compile-with "${CC} -c ${CFLAGS:N-mgeneral-regs-only} ${WERROR} ${PROF} ${.IMPSRC}" dev/acpica/acpi_bus_if.m optional acpi dev/acpica/acpi_if.m optional acpi dev/acpica/acpi_pci_link.c optional acpi pci dev/acpica/acpi_pcib.c optional acpi pci dev/acpica/acpi_pxm.c optional acpi dev/ahci/ahci_generic.c optional ahci cddl/dev/dtrace/aarch64/dtrace_asm.S optional dtrace compile-with "${DTRACE_S}" cddl/dev/dtrace/aarch64/dtrace_subr.c optional dtrace compile-with "${DTRACE_C}" cddl/dev/fbt/aarch64/fbt_isa.c optional dtrace_fbt | dtraceall compile-with "${FBT_C}" ## ## Device drivers ## dev/axgbe/if_axgbe.c optional axa dev/axgbe/xgbe-desc.c optional axa dev/axgbe/xgbe-dev.c optional axa dev/axgbe/xgbe-drv.c optional axa dev/axgbe/xgbe-mdio.c optional axa dev/axgbe/xgbe-sysctl.c optional axa dev/axgbe/xgbe-txrx.c optional axa dev/axgbe/xgbe_osdep.c optional axa dev/axgbe/xgbe-phy-v1.c optional axa dev/cpufreq/cpufreq_dt.c optional cpufreq fdt +dev/dpaa2/dpaa2_bp.c optional soc_nxp_ls dpaa2 +dev/dpaa2/dpaa2_cmd_if.m optional soc_nxp_ls dpaa2 +dev/dpaa2/dpaa2_con.c optional soc_nxp_ls dpaa2 +dev/dpaa2/dpaa2_io.c optional soc_nxp_ls dpaa2 +dev/dpaa2/dpaa2_mac.c optional soc_nxp_ls dpaa2 +dev/dpaa2/dpaa2_mc.c optional soc_nxp_ls dpaa2 +dev/dpaa2/dpaa2_mc_acpi.c optional soc_nxp_ls dpaa2 acpi +dev/dpaa2/dpaa2_mc_fdt.c optional soc_nxp_ls dpaa2 fdt +dev/dpaa2/dpaa2_mc_if.m optional soc_nxp_ls dpaa2 +dev/dpaa2/dpaa2_mcp.c optional soc_nxp_ls dpaa2 +dev/dpaa2/dpaa2_ni.c optional soc_nxp_ls dpaa2 +dev/dpaa2/dpaa2_rc.c optional soc_nxp_ls dpaa2 +dev/dpaa2/dpaa2_swp.c optional soc_nxp_ls dpaa2 +dev/dpaa2/dpaa2_swp_if.m optional soc_nxp_ls dpaa2 +dev/dpaa2/memac_mdio_acpi.c optional soc_nxp_ls dpaa2 acpi +dev/dpaa2/memac_mdio_common.c optional soc_nxp_ls dpaa2 acpi | soc_nxp_ls dpaa2 fdt +dev/dpaa2/memac_mdio_fdt.c optional soc_nxp_ls dpaa2 fdt +dev/dpaa2/memac_mdio_if.m optional soc_nxp_ls dpaa2 acpi | soc_nxp_ls dpaa2 fdt + dev/dwc/if_dwc.c optional fdt dwc_rk soc_rockchip_rk3328 | fdt dwc_rk soc_rockchip_rk3399 | fdt dwc_socfpga soc_intel_stratix10 dev/dwc/if_dwc_if.m optional fdt dwc_rk soc_rockchip_rk3328 | fdt dwc_rk soc_rockchip_rk3399 | fdt dwc_socfpga soc_intel_stratix10 dev/gpio/pl061.c optional pl061 gpio dev/gpio/pl061_acpi.c optional pl061 gpio acpi dev/gpio/pl061_fdt.c optional pl061 gpio fdt dev/hwpmc/hwpmc_arm64.c optional hwpmc dev/hwpmc/hwpmc_arm64_md.c optional hwpmc dev/ice/if_ice_iflib.c optional ice pci \ compile-with "${NORMAL_C} -I$S/dev/ice" dev/ice/ice_lib.c optional ice pci \ compile-with "${NORMAL_C} -I$S/dev/ice" dev/ice/ice_osdep.c optional ice pci \ compile-with "${NORMAL_C} -I$S/dev/ice" dev/ice/ice_resmgr.c optional ice pci \ compile-with "${NORMAL_C} -I$S/dev/ice" dev/ice/ice_strings.c optional ice pci \ compile-with "${NORMAL_C} -I$S/dev/ice" dev/ice/ice_iflib_recovery_txrx.c optional ice pci \ compile-with "${NORMAL_C} -I$S/dev/ice" dev/ice/ice_iflib_txrx.c optional ice pci \ compile-with "${NORMAL_C} -I$S/dev/ice" dev/ice/ice_common.c optional ice pci \ compile-with "${NORMAL_C} -I$S/dev/ice" dev/ice/ice_controlq.c optional ice pci \ compile-with "${NORMAL_C} -I$S/dev/ice" dev/ice/ice_dcb.c optional ice pci \ compile-with "${NORMAL_C} -I$S/dev/ice" dev/ice/ice_flex_pipe.c optional ice pci \ compile-with "${NORMAL_C} -I$S/dev/ice" dev/ice/ice_flow.c optional ice pci \ compile-with "${NORMAL_C} -I$S/dev/ice" dev/ice/ice_nvm.c optional ice pci \ compile-with "${NORMAL_C} -I$S/dev/ice" dev/ice/ice_sched.c optional ice pci \ compile-with "${NORMAL_C} -I$S/dev/ice" dev/ice/ice_switch.c optional ice pci \ compile-with "${NORMAL_C} -I$S/dev/ice" dev/ice/ice_vlan_mode.c optional ice pci \ compile-with "${NORMAL_C} -I$S/dev/ice" dev/ice/ice_fw_logging.c optional ice pci \ compile-with "${NORMAL_C} -I$S/dev/ice" dev/ice/ice_fwlog.c optional ice pci \ compile-with "${NORMAL_C} -I$S/dev/ice" dev/ice/ice_rdma.c optional ice pci \ compile-with "${NORMAL_C} -I$S/dev/ice" dev/ice/irdma_if.m optional ice pci \ compile-with "${NORMAL_M} -I$S/dev/ice" dev/ice/irdma_di_if.m optional ice pci \ compile-with "${NORMAL_M} -I$S/dev/ice" ice_ddp.c optional ice_ddp \ compile-with "${AWK} -f $S/tools/fw_stub.awk ice_ddp.fw:ice_ddp:0x01031b00 -mice_ddp -c${.TARGET}" \ no-ctfconvert no-implicit-rule before-depend local \ clean "ice_ddp.c" ice_ddp.fwo optional ice_ddp \ dependency "ice_ddp.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "ice_ddp.fwo" ice_ddp.fw optional ice_ddp \ dependency "$S/contrib/dev/ice/ice-1.3.27.0.pkg" \ compile-with "${CP} $S/contrib/dev/ice/ice-1.3.27.0.pkg ice_ddp.fw" \ no-obj no-implicit-rule \ clean "ice_ddp.fw" dev/iicbus/sy8106a.c optional sy8106a fdt dev/iicbus/twsi/mv_twsi.c optional twsi fdt dev/iicbus/twsi/a10_twsi.c optional twsi fdt dev/iicbus/twsi/twsi.c optional twsi fdt dev/ipmi/ipmi.c optional ipmi dev/ipmi/ipmi_acpi.c optional ipmi acpi dev/ipmi/ipmi_kcs.c optional ipmi dev/ipmi/ipmi_smic.c optional ipmi dev/mbox/mbox_if.m optional soc_brcm_bcm2837 dev/mmc/host/dwmmc.c optional dwmmc fdt dev/mmc/host/dwmmc_altera.c optional dwmmc dwmmc_altera fdt dev/mmc/host/dwmmc_hisi.c optional dwmmc dwmmc_hisi fdt dev/mmc/host/dwmmc_rockchip.c optional dwmmc rk_dwmmc fdt dev/neta/if_mvneta_fdt.c optional neta fdt dev/neta/if_mvneta.c optional neta mdio mii dev/ofw/ofw_cpu.c optional fdt dev/ofw/ofwpci.c optional fdt pci dev/pci/controller/pci_n1sdp.c optional pci_n1sdp acpi dev/pci/pci_host_generic.c optional pci dev/pci/pci_host_generic_acpi.c optional pci acpi dev/pci/pci_host_generic_fdt.c optional pci fdt dev/pci/pci_dw_mv.c optional pci fdt dev/pci/pci_dw.c optional pci fdt dev/pci/pci_dw_if.m optional pci fdt dev/psci/psci.c standard dev/psci/smccc_arm64.S standard dev/psci/smccc.c standard dev/safexcel/safexcel.c optional safexcel fdt dev/sdhci/sdhci_xenon.c optional sdhci_xenon sdhci dev/sdhci/sdhci_xenon_acpi.c optional sdhci_xenon sdhci acpi dev/sdhci/sdhci_xenon_fdt.c optional sdhci_xenon sdhci fdt dev/uart/uart_cpu_arm64.c optional uart dev/uart/uart_dev_mu.c optional uart uart_mu dev/uart/uart_dev_pl011.c optional uart pl011 dev/usb/controller/dwc_otg_hisi.c optional dwcotg fdt soc_hisi_hi6220 dev/usb/controller/dwc3.c optional xhci acpi dwc3 | xhci fdt dwc3 dev/usb/controller/ehci_mv.c optional ehci_mv fdt dev/usb/controller/generic_ehci.c optional ehci dev/usb/controller/generic_ehci_acpi.c optional ehci acpi dev/usb/controller/generic_ehci_fdt.c optional ehci fdt dev/usb/controller/generic_ohci.c optional ohci fdt dev/usb/controller/generic_usb_if.m optional ohci fdt dev/usb/controller/musb_otg_allwinner.c optional musb fdt soc_allwinner_a64 dev/usb/controller/usb_nop_xceiv.c optional fdt dev/usb/controller/generic_xhci.c optional xhci dev/usb/controller/generic_xhci_acpi.c optional xhci acpi dev/usb/controller/generic_xhci_fdt.c optional xhci fdt dev/vnic/mrml_bridge.c optional vnic fdt dev/vnic/nic_main.c optional vnic pci dev/vnic/nicvf_main.c optional vnic pci pci_iov dev/vnic/nicvf_queues.c optional vnic pci pci_iov dev/vnic/thunder_bgx_fdt.c optional soc_cavm_thunderx pci vnic fdt dev/vnic/thunder_bgx.c optional soc_cavm_thunderx pci vnic pci dev/vnic/thunder_mdio_fdt.c optional soc_cavm_thunderx pci vnic fdt dev/vnic/thunder_mdio.c optional soc_cavm_thunderx pci vnic dev/vnic/lmac_if.m optional inet | inet6 | vnic ## ## SoC Support ## # Allwinner common files arm/allwinner/a10_timer.c optional a10_timer fdt arm/allwinner/a10_codec.c optional sound a10_codec arm/allwinner/a31_dmac.c optional a31_dmac arm/allwinner/sunxi_dma_if.m optional a31_dmac arm/allwinner/aw_cir.c optional evdev aw_cir fdt arm/allwinner/aw_dwc3.c optional aw_dwc3 fdt arm/allwinner/aw_gpio.c optional gpio aw_gpio fdt arm/allwinner/aw_mmc.c optional mmc aw_mmc fdt | mmccam aw_mmc fdt arm/allwinner/aw_nmi.c optional aw_nmi fdt \ compile-with "${NORMAL_C} -I$S/contrib/device-tree/include" arm/allwinner/aw_pwm.c optional aw_pwm fdt arm/allwinner/aw_r_intc.c optional aw_r_intc fdt arm/allwinner/aw_rsb.c optional aw_rsb fdt arm/allwinner/aw_rtc.c optional aw_rtc fdt arm/allwinner/aw_sid.c optional aw_sid nvmem fdt arm/allwinner/aw_spi.c optional aw_spi fdt arm/allwinner/aw_syscon.c optional aw_syscon syscon fdt arm/allwinner/aw_thermal.c optional aw_thermal nvmem fdt arm/allwinner/aw_usbphy.c optional ehci aw_usbphy fdt arm/allwinner/aw_usb3phy.c optional xhci aw_usbphy fdt arm/allwinner/aw_wdog.c optional aw_wdog fdt arm/allwinner/axp81x.c optional axp81x fdt arm/allwinner/if_awg.c optional awg syscon aw_sid nvmem fdt # Allwinner clock driver arm/allwinner/clkng/aw_ccung.c optional aw_ccu fdt arm/allwinner/clkng/aw_clk_frac.c optional aw_ccu fdt arm/allwinner/clkng/aw_clk_m.c optional aw_ccu fdt arm/allwinner/clkng/aw_clk_mipi.c optional aw_ccu fdt arm/allwinner/clkng/aw_clk_nkmp.c optional aw_ccu fdt arm/allwinner/clkng/aw_clk_nm.c optional aw_ccu fdt arm/allwinner/clkng/aw_clk_nmm.c optional aw_ccu fdt arm/allwinner/clkng/aw_clk_np.c optional aw_ccu fdt arm/allwinner/clkng/aw_clk_prediv_mux.c optional aw_ccu fdt arm/allwinner/clkng/ccu_a64.c optional soc_allwinner_a64 aw_ccu fdt arm/allwinner/clkng/ccu_h3.c optional soc_allwinner_h5 aw_ccu fdt arm/allwinner/clkng/ccu_h6.c optional soc_allwinner_h6 aw_ccu fdt arm/allwinner/clkng/ccu_h6_r.c optional soc_allwinner_h6 aw_ccu fdt arm/allwinner/clkng/ccu_sun8i_r.c optional aw_ccu fdt arm/allwinner/clkng/ccu_de2.c optional aw_ccu fdt # Allwinner padconf files arm/allwinner/a64/a64_padconf.c optional soc_allwinner_a64 fdt arm/allwinner/a64/a64_r_padconf.c optional soc_allwinner_a64 fdt arm/allwinner/h3/h3_padconf.c optional soc_allwinner_h5 fdt arm/allwinner/h3/h3_r_padconf.c optional soc_allwinner_h5 fdt arm/allwinner/h6/h6_padconf.c optional soc_allwinner_h6 fdt arm/allwinner/h6/h6_r_padconf.c optional soc_allwinner_h6 fdt # Altera/Intel dev/altera/dwc/if_dwc_socfpga.c optional fdt dwc_socfpga arm64/intel/firmware.c optional soc_intel_stratix10 arm64/intel/stratix10-soc-fpga-mgr.c optional soc_intel_stratix10 arm64/intel/stratix10-svc.c optional soc_intel_stratix10 # Annapurna arm/annapurna/alpine/alpine_ccu.c optional al_ccu fdt arm/annapurna/alpine/alpine_nb_service.c optional al_nb_service fdt arm/annapurna/alpine/alpine_pci.c optional al_pci fdt arm/annapurna/alpine/alpine_pci_msix.c optional al_pci fdt arm/annapurna/alpine/alpine_serdes.c optional al_serdes fdt \ no-depend \ compile-with "${CC} -c -o ${.TARGET} ${CFLAGS} -I$S/contrib/alpine-hal -I$S/contrib/alpine-hal/eth ${PROF} ${.IMPSRC}" # Broadcom arm64/broadcom/brcmmdio/mdio_mux_iproc.c optional soc_brcm_ns2 fdt arm64/broadcom/brcmmdio/mdio_nexus_iproc.c optional soc_brcm_ns2 fdt arm64/broadcom/brcmmdio/mdio_ns2_pcie_phy.c optional soc_brcm_ns2 fdt pci arm64/broadcom/genet/if_genet.c optional SOC_BRCM_BCM2838 fdt genet arm/broadcom/bcm2835/bcm2835_audio.c optional sound vchiq fdt \ compile-with "${NORMAL_C} -DUSE_VCHIQ_ARM -D__VCCOREVER__=0x04000000 -I$S/contrib/vchiq" arm/broadcom/bcm2835/bcm2835_bsc.c optional bcm2835_bsc fdt arm/broadcom/bcm2835/bcm2835_clkman.c optional soc_brcm_bcm2837 fdt | soc_brcm_bcm2838 fdt arm/broadcom/bcm2835/bcm2835_cpufreq.c optional soc_brcm_bcm2837 fdt | soc_brcm_bcm2838 fdt arm/broadcom/bcm2835/bcm2835_dma.c optional soc_brcm_bcm2837 fdt | soc_brcm_bcm2838 fdt arm/broadcom/bcm2835/bcm2835_fbd.c optional vt soc_brcm_bcm2837 fdt | vt soc_brcm_bcm2838 fdt arm/broadcom/bcm2835/bcm2835_firmware.c optional soc_brcm_bcm2837 fdt | soc_brcm_bcm2838 fdt arm/broadcom/bcm2835/bcm2835_ft5406.c optional evdev bcm2835_ft5406 fdt arm/broadcom/bcm2835/bcm2835_gpio.c optional gpio soc_brcm_bcm2837 fdt | gpio soc_brcm_bcm2838 fdt arm/broadcom/bcm2835/bcm2835_intr.c optional soc_brcm_bcm2837 fdt | soc_brcm_bcm2838 fdt arm/broadcom/bcm2835/bcm2835_mbox.c optional soc_brcm_bcm2837 fdt | soc_brcm_bcm2838 fdt arm/broadcom/bcm2835/bcm2835_rng.c optional !random_loadable soc_brcm_bcm2837 fdt | !random_loadable soc_brcm_bcm2838 fdt arm/broadcom/bcm2835/bcm2835_sdhci.c optional sdhci soc_brcm_bcm2837 fdt | sdhci soc_brcm_bcm2838 fdt arm/broadcom/bcm2835/bcm2835_sdhost.c optional sdhci soc_brcm_bcm2837 fdt | sdhci soc_brcm_bcm2838 fdt arm/broadcom/bcm2835/bcm2835_spi.c optional bcm2835_spi fdt arm/broadcom/bcm2835/bcm2835_vcbus.c optional soc_brcm_bcm2837 fdt | soc_brcm_bcm2838 fdt arm/broadcom/bcm2835/bcm2835_vcio.c optional soc_brcm_bcm2837 fdt | soc_brcm_bcm2838 fdt arm/broadcom/bcm2835/bcm2835_wdog.c optional soc_brcm_bcm2837 fdt | soc_brcm_bcm2838 fdt arm/broadcom/bcm2835/bcm2836.c optional soc_brcm_bcm2837 fdt | soc_brcm_bcm2838 fdt arm/broadcom/bcm2835/bcm283x_dwc_fdt.c optional dwcotg fdt soc_brcm_bcm2837 | dwcotg fdt soc_brcm_bcm2838 arm/broadcom/bcm2835/bcm2838_pci.c optional soc_brcm_bcm2838 fdt pci arm/broadcom/bcm2835/bcm2838_xhci.c optional soc_brcm_bcm2838 fdt pci xhci arm/broadcom/bcm2835/raspberrypi_gpio.c optional soc_brcm_bcm2837 gpio | soc_brcm_bcm2838 gpio contrib/vchiq/interface/compat/vchi_bsd.c optional vchiq soc_brcm_bcm2837 \ compile-with "${NORMAL_C} -DUSE_VCHIQ_ARM -D__VCCOREVER__=0x04000000 -I$S/contrib/vchiq" contrib/vchiq/interface/vchiq_arm/vchiq_2835_arm.c optional vchiq soc_brcm_bcm2837 \ compile-with "${NORMAL_C} -Wno-unused -DUSE_VCHIQ_ARM -D__VCCOREVER__=0x04000000 -I$S/contrib/vchiq" contrib/vchiq/interface/vchiq_arm/vchiq_arm.c optional vchiq soc_brcm_bcm2837 \ compile-with "${NORMAL_C} -Wno-unused -DUSE_VCHIQ_ARM -D__VCCOREVER__=0x04000000 -I$S/contrib/vchiq" contrib/vchiq/interface/vchiq_arm/vchiq_connected.c optional vchiq soc_brcm_bcm2837 \ compile-with "${NORMAL_C} -DUSE_VCHIQ_ARM -D__VCCOREVER__=0x04000000 -I$S/contrib/vchiq" contrib/vchiq/interface/vchiq_arm/vchiq_core.c optional vchiq soc_brcm_bcm2837 \ compile-with "${NORMAL_C} -DUSE_VCHIQ_ARM -D__VCCOREVER__=0x04000000 -I$S/contrib/vchiq" contrib/vchiq/interface/vchiq_arm/vchiq_kern_lib.c optional vchiq soc_brcm_bcm2837 \ compile-with "${NORMAL_C} -DUSE_VCHIQ_ARM -D__VCCOREVER__=0x04000000 -I$S/contrib/vchiq" contrib/vchiq/interface/vchiq_arm/vchiq_kmod.c optional vchiq soc_brcm_bcm2837 \ compile-with "${NORMAL_C} -DUSE_VCHIQ_ARM -D__VCCOREVER__=0x04000000 -I$S/contrib/vchiq" contrib/vchiq/interface/vchiq_arm/vchiq_shim.c optional vchiq soc_brcm_bcm2837 \ compile-with "${NORMAL_C} -DUSE_VCHIQ_ARM -D__VCCOREVER__=0x04000000 -I$S/contrib/vchiq" contrib/vchiq/interface/vchiq_arm/vchiq_util.c optional vchiq soc_brcm_bcm2837 \ compile-with "${NORMAL_C} -DUSE_VCHIQ_ARM -D__VCCOREVER__=0x04000000 -I$S/contrib/vchiq" # Cavium arm64/cavium/thunder_pcie_fdt.c optional soc_cavm_thunderx pci fdt arm64/cavium/thunder_pcie_pem.c optional soc_cavm_thunderx pci arm64/cavium/thunder_pcie_pem_fdt.c optional soc_cavm_thunderx pci fdt arm64/cavium/thunder_pcie_common.c optional soc_cavm_thunderx pci # i.MX8 Clock support arm64/freescale/imx/imx8mq_ccm.c optional fdt soc_freescale_imx8 arm64/freescale/imx/clk/imx_clk_gate.c optional fdt soc_freescale_imx8 arm64/freescale/imx/clk/imx_clk_mux.c optional fdt soc_freescale_imx8 arm64/freescale/imx/clk/imx_clk_composite.c optional fdt soc_freescale_imx8 arm64/freescale/imx/clk/imx_clk_sscg_pll.c optional fdt soc_freescale_imx8 arm64/freescale/imx/clk/imx_clk_frac_pll.c optional fdt soc_freescale_imx8 # iMX drivers arm/freescale/imx/imx_gpio.c optional gpio soc_freescale_imx8 arm/freescale/imx/imx_i2c.c optional fsliic arm/freescale/imx/imx_machdep.c optional fdt soc_freescale_imx8 arm64/freescale/imx/imx7gpc.c optional fdt soc_freescale_imx8 dev/ffec/if_ffec.c optional ffec # Marvell arm/mv/a37x0_gpio.c optional a37x0_gpio gpio fdt arm/mv/a37x0_iic.c optional a37x0_iic iicbus fdt arm/mv/a37x0_spi.c optional a37x0_spi spibus fdt arm/mv/armada38x/armada38x_rtc.c optional mv_rtc fdt arm/mv/gpio.c optional mv_gpio fdt arm/mv/mvebu_gpio.c optional mv_gpio fdt arm/mv/mvebu_pinctrl.c optional mvebu_pinctrl fdt arm/mv/mv_ap806_clock.c optional SOC_MARVELL_8K fdt arm/mv/mv_ap806_gicp.c optional mv_ap806_gicp fdt arm/mv/mv_ap806_sei.c optional mv_ap806_sei fdt arm/mv/mv_cp110_clock.c optional SOC_MARVELL_8K fdt arm/mv/mv_cp110_icu.c optional mv_cp110_icu fdt arm/mv/mv_cp110_icu_bus.c optional mv_cp110_icu fdt arm/mv/mv_thermal.c optional SOC_MARVELL_8K mv_thermal fdt arm/mv/armada38x/armada38x_rtc.c optional mv_rtc fdt # NVidia arm/nvidia/tegra_abpmisc.c optional fdt soc_nvidia_tegra210 arm/nvidia/tegra_ahci.c optional fdt soc_nvidia_tegra210 arm/nvidia/tegra_efuse.c optional fdt soc_nvidia_tegra210 arm/nvidia/tegra_ehci.c optional fdt soc_nvidia_tegra210 arm/nvidia/tegra_gpio.c optional fdt soc_nvidia_tegra210 arm/nvidia/tegra_i2c.c optional fdt soc_nvidia_tegra210 arm/nvidia/tegra_lic.c optional fdt soc_nvidia_tegra210 arm/nvidia/tegra_mc.c optional fdt soc_nvidia_tegra210 arm/nvidia/tegra_pcie.c optional fdt soc_nvidia_tegra210 arm/nvidia/tegra_sdhci.c optional fdt soc_nvidia_tegra210 arm/nvidia/tegra_soctherm_if.m optional fdt soc_nvidia_tegra210 arm/nvidia/tegra_soctherm.c optional fdt soc_nvidia_tegra210 arm/nvidia/tegra_uart.c optional fdt soc_nvidia_tegra210 arm/nvidia/tegra_usbphy.c optional fdt soc_nvidia_tegra210 arm/nvidia/tegra_xhci.c optional fdt soc_nvidia_tegra210 arm64/nvidia/tegra210/max77620.c optional fdt soc_nvidia_tegra210 arm64/nvidia/tegra210/max77620_gpio.c optional fdt soc_nvidia_tegra210 arm64/nvidia/tegra210/max77620_regulators.c optional fdt soc_nvidia_tegra210 arm64/nvidia/tegra210/max77620_rtc.c optional fdt soc_nvidia_tegra210 arm64/nvidia/tegra210/tegra210_car.c optional fdt soc_nvidia_tegra210 arm64/nvidia/tegra210/tegra210_clk_per.c optional fdt soc_nvidia_tegra210 arm64/nvidia/tegra210/tegra210_clk_pll.c optional fdt soc_nvidia_tegra210 arm64/nvidia/tegra210/tegra210_clk_super.c optional fdt soc_nvidia_tegra210 arm64/nvidia/tegra210/tegra210_coretemp.c optional fdt soc_nvidia_tegra210 arm64/nvidia/tegra210/tegra210_cpufreq.c optional fdt soc_nvidia_tegra210 arm64/nvidia/tegra210/tegra210_pinmux.c optional fdt soc_nvidia_tegra210 arm64/nvidia/tegra210/tegra210_pmc.c optional fdt soc_nvidia_tegra210 arm64/nvidia/tegra210/tegra210_xusbpadctl.c optional fdt soc_nvidia_tegra210 # Nvidia firmware for Tegra tegra210_xusb_fw.c optional tegra210_xusb_fw \ dependency "$S/conf/files.arm64" \ compile-with "${AWK} -f $S/tools/fw_stub.awk tegra210_xusb.fw:tegra210_xusb_fw -mtegra210_xusb_fw -c${.TARGET}" \ no-ctfconvert no-implicit-rule before-depend local \ clean "tegra210_xusb_fw.c" tegra210_xusb.fwo optional tegra210_xusb_fw \ dependency "tegra210_xusb.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "tegra210_xusb.fwo" tegra210_xusb.fw optional tegra210_xusb_fw \ dependency "$S/contrib/dev/nvidia/tegra210_xusb.bin.uu" \ compile-with "${NORMAL_FW}" \ no-obj no-implicit-rule \ clean "tegra210_xusb.fw" # NXP arm/freescale/vybrid/vf_i2c.c optional vf_i2c iicbus SOC_NXP_LS arm64/qoriq/ls1046_gpio.c optional ls1046_gpio gpio fdt SOC_NXP_LS arm64/qoriq/qoriq_dw_pci.c optional pci fdt SOC_NXP_LS arm64/qoriq/qoriq_therm.c optional pci fdt SOC_NXP_LS arm64/qoriq/qoriq_therm_if.m optional pci fdt SOC_NXP_LS arm64/qoriq/clk/ls1046a_clkgen.c optional clk SOC_NXP_LS arm64/qoriq/clk/ls1088a_clkgen.c optional clk SOC_NXP_LS arm64/qoriq/clk/lx2160a_clkgen.c optional clk SOC_NXP_LS arm64/qoriq/clk/qoriq_clk_pll.c optional clk SOC_NXP_LS arm64/qoriq/clk/qoriq_clkgen.c optional clk SOC_NXP_LS dev/ahci/ahci_fsl_fdt.c optional SOC_NXP_LS ahci fdt # Qualcomm arm64/qualcomm/qcom_gcc.c optional qcom_gcc fdt # RockChip Drivers arm64/rockchip/rk3399_emmcphy.c optional fdt rk_emmcphy soc_rockchip_rk3399 arm64/rockchip/rk_dwc3.c optional fdt rk_dwc3 soc_rockchip_rk3399 arm64/rockchip/rk_i2c.c optional fdt rk_i2c soc_rockchip_rk3328 | fdt rk_i2c soc_rockchip_rk3399 arm64/rockchip/rk805.c optional fdt rk805 soc_rockchip_rk3328 | fdt rk805 soc_rockchip_rk3399 arm64/rockchip/rk_grf.c optional fdt soc_rockchip_rk3328 | fdt soc_rockchip_rk3399 arm64/rockchip/rk_pinctrl.c optional fdt rk_pinctrl soc_rockchip_rk3328 | fdt rk_pinctrl soc_rockchip_rk3399 arm64/rockchip/rk_gpio.c optional fdt rk_gpio soc_rockchip_rk3328 | fdt rk_gpio soc_rockchip_rk3399 arm64/rockchip/rk_iodomain.c optional fdt rk_iodomain arm64/rockchip/rk_spi.c optional fdt rk_spi arm64/rockchip/rk_usb2phy.c optional fdt rk_usb2phy soc_rockchip_rk3328 | soc_rockchip_rk3399 arm64/rockchip/rk_typec_phy.c optional fdt rk_typec_phy soc_rockchip_rk3399 arm64/rockchip/if_dwc_rk.c optional fdt dwc_rk soc_rockchip_rk3328 | fdt dwc_rk soc_rockchip_rk3399 arm64/rockchip/rk_tsadc_if.m optional fdt soc_rockchip_rk3399 arm64/rockchip/rk_tsadc.c optional fdt soc_rockchip_rk3399 arm64/rockchip/rk_pwm.c optional fdt rk_pwm arm64/rockchip/rk_pcie.c optional fdt pci soc_rockchip_rk3399 arm64/rockchip/rk_pcie_phy.c optional fdt pci soc_rockchip_rk3399 # RockChip Clock support arm64/rockchip/clk/rk_cru.c optional fdt soc_rockchip_rk3328 | fdt soc_rockchip_rk3399 arm64/rockchip/clk/rk_clk_armclk.c optional fdt soc_rockchip_rk3328 | fdt soc_rockchip_rk3399 arm64/rockchip/clk/rk_clk_composite.c optional fdt soc_rockchip_rk3328 | fdt soc_rockchip_rk3399 arm64/rockchip/clk/rk_clk_fract.c optional fdt soc_rockchip_rk3328 | fdt soc_rockchip_rk3399 arm64/rockchip/clk/rk_clk_gate.c optional fdt soc_rockchip_rk3328 | fdt soc_rockchip_rk3399 arm64/rockchip/clk/rk_clk_mux.c optional fdt soc_rockchip_rk3328 | fdt soc_rockchip_rk3399 arm64/rockchip/clk/rk_clk_pll.c optional fdt soc_rockchip_rk3328 | fdt soc_rockchip_rk3399 arm64/rockchip/clk/rk3328_cru.c optional fdt soc_rockchip_rk3328 arm64/rockchip/clk/rk3399_cru.c optional fdt soc_rockchip_rk3399 arm64/rockchip/clk/rk3399_pmucru.c optional fdt soc_rockchip_rk3399 # Xilinx arm/xilinx/uart_dev_cdnc.c optional uart soc_xilinx_zynq # Linuxkpi compat/linuxkpi/common/src/linux_fpu.c optional compat_linuxkpi \ compile-with "${LINUXKPI_C}" # Cloudabi arm64/cloudabi32/cloudabi32_sysvec.c optional compat_cloudabi32 arm64/cloudabi64/cloudabi64_sysvec.c optional compat_cloudabi64 cloudabi32_vdso.o optional compat_cloudabi32 \ dependency "$S/contrib/cloudabi/cloudabi_vdso_armv6_on_64bit.S" \ compile-with "${CC} -x assembler-with-cpp -m32 -shared -nostdinc -nostdlib -Wl,-T$S/compat/cloudabi/cloudabi_vdso.lds $S/contrib/cloudabi/cloudabi_vdso_armv6_on_64bit.S -o ${.TARGET}" \ no-obj no-implicit-rule \ clean "cloudabi32_vdso.o" cloudabi32_vdso_blob.o optional compat_cloudabi32 \ dependency "cloudabi32_vdso.o" \ compile-with "${OBJCOPY} --input-target binary --output-target elf64-littleaarch64 --binary-architecture aarch64 cloudabi32_vdso.o ${.TARGET}" \ no-implicit-rule \ clean "cloudabi32_vdso_blob.o" cloudabi64_vdso.o optional compat_cloudabi64 \ dependency "$S/contrib/cloudabi/cloudabi_vdso_aarch64.S" \ compile-with "${CC} -x assembler-with-cpp -shared -nostdinc -nostdlib -Wl,-T$S/compat/cloudabi/cloudabi_vdso.lds $S/contrib/cloudabi/cloudabi_vdso_aarch64.S -o ${.TARGET}" \ no-obj no-implicit-rule \ clean "cloudabi64_vdso.o" cloudabi64_vdso_blob.o optional compat_cloudabi64 \ dependency "cloudabi64_vdso.o" \ compile-with "${OBJCOPY} --input-target binary --output-target elf64-littleaarch64 --binary-architecture aarch64 cloudabi64_vdso.o ${.TARGET}" \ no-implicit-rule \ clean "cloudabi64_vdso_blob.o" diff --git a/sys/dev/dpaa2/dpaa2_bp.c b/sys/dev/dpaa2/dpaa2_bp.c new file mode 100644 index 000000000000..78e1ca68cdb1 --- /dev/null +++ b/sys/dev/dpaa2/dpaa2_bp.c @@ -0,0 +1,205 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright © 2021-2022 Dmitry Salychev + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +/* + * The DPAA2 Buffer Pool (DPBP) driver. + * + * The DPBP configures a buffer pool that can be associated with DPAA2 network + * and accelerator interfaces. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#include "pcib_if.h" +#include "pci_if.h" + +#include "dpaa2_mc.h" +#include "dpaa2_mcp.h" +#include "dpaa2_swp.h" +#include "dpaa2_swp_if.h" +#include "dpaa2_cmd_if.h" + +/* DPAA2 Buffer Pool resource specification. */ +struct resource_spec dpaa2_bp_spec[] = { + /* + * DPMCP resources. + * + * NOTE: MC command portals (MCPs) are used to send commands to, and + * receive responses from, the MC firmware. One portal per DPBP. + */ +#define MCP_RES_NUM (1u) +#define MCP_RID_OFF (0u) +#define MCP_RID(rid) ((rid) + MCP_RID_OFF) + /* --- */ + { DPAA2_DEV_MCP, MCP_RID(0), RF_ACTIVE | RF_SHAREABLE | RF_OPTIONAL }, + /* --- */ + RESOURCE_SPEC_END +}; + +static int +dpaa2_bp_probe(device_t dev) +{ + /* DPBP device will be added by the parent resource container. */ + device_set_desc(dev, "DPAA2 Buffer Pool"); + return (BUS_PROBE_DEFAULT); +} + +static int +dpaa2_bp_detach(device_t dev) +{ + device_t child = dev; + struct dpaa2_bp_softc *sc = device_get_softc(dev); + struct dpaa2_devinfo *dinfo = device_get_ivars(dev); + + if (sc->cmd != NULL) { + (void)DPAA2_CMD_BP_DISABLE(dev, child, sc->cmd); + (void)DPAA2_CMD_BP_CLOSE(dev, child, dpaa2_mcp_tk(sc->cmd, + sc->bp_token)); + (void)DPAA2_CMD_RC_CLOSE(dev, child, dpaa2_mcp_tk(sc->cmd, + sc->rc_token)); + + dpaa2_mcp_free_command(sc->cmd); + sc->cmd = NULL; + } + + dinfo->portal = NULL; + bus_release_resources(sc->dev, dpaa2_bp_spec, sc->res); + + return (0); +} + +static int +dpaa2_bp_attach(device_t dev) +{ + device_t pdev = device_get_parent(dev); + device_t child = dev; + device_t mcp_dev; + struct dpaa2_bp_softc *sc = device_get_softc(dev); + struct dpaa2_devinfo *rcinfo = device_get_ivars(pdev); + struct dpaa2_devinfo *dinfo = device_get_ivars(dev); + struct dpaa2_devinfo *mcp_dinfo; + int error; + + sc->dev = dev; + sc->cmd = NULL; + + error = bus_alloc_resources(sc->dev, dpaa2_bp_spec, sc->res); + if (error) { + device_printf(dev, "%s: failed to allocate resources: " + "error=%d\n", __func__, error); + return (ENXIO); + } + + /* Send commands to MC via allocated portal. */ + mcp_dev = (device_t) rman_get_start(sc->res[MCP_RID(0)]); + mcp_dinfo = device_get_ivars(mcp_dev); + dinfo->portal = mcp_dinfo->portal; + + /* Allocate a command to send to MC hardware. */ + error = dpaa2_mcp_init_command(&sc->cmd, DPAA2_CMD_DEF); + if (error) { + device_printf(dev, "%s: failed to allocate dpaa2_cmd: " + "error=%d\n", __func__, error); + dpaa2_bp_detach(dev); + return (ENXIO); + } + + /* Open resource container and DPBP object. */ + error = DPAA2_CMD_RC_OPEN(dev, child, sc->cmd, rcinfo->id, + &sc->rc_token); + if (error) { + device_printf(dev, "%s: failed to open DPRC: error=%d\n", + __func__, error); + dpaa2_bp_detach(dev); + return (ENXIO); + } + error = DPAA2_CMD_BP_OPEN(dev, child, sc->cmd, dinfo->id, &sc->bp_token); + if (error) { + device_printf(dev, "%s: failed to open DPBP: id=%d, error=%d\n", + __func__, dinfo->id, error); + dpaa2_bp_detach(dev); + return (ENXIO); + } + + /* Prepare DPBP object. */ + error = DPAA2_CMD_BP_RESET(dev, child, sc->cmd); + if (error) { + device_printf(dev, "%s: failed to reset DPBP: id=%d, error=%d\n", + __func__, dinfo->id, error); + dpaa2_bp_detach(dev); + return (ENXIO); + } + error = DPAA2_CMD_BP_ENABLE(dev, child, sc->cmd); + if (error) { + device_printf(dev, "%s: failed to enable DPBP: id=%d, " + "error=%d\n", __func__, dinfo->id, error); + dpaa2_bp_detach(dev); + return (ENXIO); + } + error = DPAA2_CMD_BP_GET_ATTRIBUTES(dev, child, sc->cmd, &sc->attr); + if (error) { + device_printf(dev, "%s: failed to get DPBP attributes: id=%d, " + "error=%d\n", __func__, dinfo->id, error); + dpaa2_bp_detach(dev); + return (ENXIO); + } + + return (0); +} + +static device_method_t dpaa2_bp_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, dpaa2_bp_probe), + DEVMETHOD(device_attach, dpaa2_bp_attach), + DEVMETHOD(device_detach, dpaa2_bp_detach), + + DEVMETHOD_END +}; + +static driver_t dpaa2_bp_driver = { + "dpaa2_bp", + dpaa2_bp_methods, + sizeof(struct dpaa2_bp_softc), +}; + +DRIVER_MODULE(dpaa2_bp, dpaa2_rc, dpaa2_bp_driver, 0, 0); diff --git a/sys/dev/dpaa2/dpaa2_bp.h b/sys/dev/dpaa2/dpaa2_bp.h new file mode 100644 index 000000000000..3ba7196eb030 --- /dev/null +++ b/sys/dev/dpaa2/dpaa2_bp.h @@ -0,0 +1,74 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright © 2022 Dmitry Salychev + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _DPAA2_BP_H +#define _DPAA2_BP_H + +#include + +/* Maximum resources per DPBP: 1 DPMCP. */ +#define DPAA2_BP_MAX_RESOURCES 1 + +/** + * @brief Attributes of the DPBP object. + * + * id: DPBP object ID. + * bpid: Hardware buffer pool ID; should be used as an argument in + * acquire/release operations on buffers. + */ +struct dpaa2_bp_attr { + uint32_t id; + uint16_t bpid; +}; + +/** + * @brief Configuration/state of the buffer pool. + */ +struct dpaa2_bp_conf { + uint8_t bdi; + uint8_t state; /* bitmask */ + uint32_t free_bufn; +}; + +/** + * @brief Software context for the DPAA2 Buffer Pool driver. + */ +struct dpaa2_bp_softc { + device_t dev; + struct dpaa2_bp_attr attr; + + /* Help to send commands to MC. */ + struct dpaa2_cmd *cmd; + uint16_t rc_token; + uint16_t bp_token; + + struct resource *res[DPAA2_BP_MAX_RESOURCES]; +}; + +extern struct resource_spec dpaa2_bp_spec[]; + +#endif /* _DPAA2_BP_H */ diff --git a/sys/dev/dpaa2/dpaa2_cmd_if.m b/sys/dev/dpaa2/dpaa2_cmd_if.m new file mode 100644 index 000000000000..96031d4ae8c5 --- /dev/null +++ b/sys/dev/dpaa2/dpaa2_cmd_if.m @@ -0,0 +1,1583 @@ +#- +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright © 2021-2022 Dmitry Salychev +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +#include +#include +#include +#include + +/** + * @brief DPAA2 MC command interface. + * + * The primary purpose of the MC provided DPAA2 objects is to simplify DPAA2 + * hardware block usage through abstraction and encapsulation. + */ +INTERFACE dpaa2_cmd; + +# +# Default implementation of the commands. +# +CODE { + static void + panic_on_mc(device_t dev) + { + if (strcmp(device_get_name(dev), "dpaa2_mc") == 0) + panic("No one can handle a command above DPAA2 MC"); + } + + static int + bypass_mng_get_version(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint32_t *major, uint32_t *minor, uint32_t *rev) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_MNG_GET_VERSION(device_get_parent(dev), child, + cmd, major, minor, rev)); + return (ENXIO); + } + static int + bypass_mng_get_soc_version(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint32_t *pvr, uint32_t *svr) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_MNG_GET_SOC_VERSION( + device_get_parent(dev), child, cmd, pvr, svr)); + return (ENXIO); + } + static int + bypass_mng_get_container_id(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint32_t *cont_id) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_MNG_GET_CONTAINER_ID( + device_get_parent(dev), child, cmd, cont_id)); + return (ENXIO); + } + static int + bypass_rc_open(device_t dev, device_t child, struct dpaa2_cmd *cmd, uint32_t cont_id, + uint16_t *token) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_RC_OPEN( + device_get_parent(dev), child, cmd, cont_id, token)); + return (ENXIO); + } + static int + bypass_rc_close(device_t dev, device_t child, struct dpaa2_cmd *cmd) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_RC_CLOSE( + device_get_parent(dev), child, cmd)); + return (ENXIO); + } + static int + bypass_rc_get_obj_count(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint32_t *obj_count) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_RC_GET_OBJ_COUNT( + device_get_parent(dev), child, cmd, obj_count)); + return (ENXIO); + } + static int + bypass_rc_get_obj(device_t dev, device_t child, struct dpaa2_cmd *cmd, uint32_t obj_idx, + struct dpaa2_obj *obj) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_RC_GET_OBJ( + device_get_parent(dev), child, cmd, obj_idx, obj)); + return (ENXIO); + } + static int + bypass_rc_get_obj_descriptor(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint32_t obj_id, enum dpaa2_dev_type type, struct dpaa2_obj *obj) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_RC_GET_OBJ_DESCRIPTOR( + device_get_parent(dev), child, cmd, obj_id, type, obj)); + return (ENXIO); + } + static int + bypass_rc_get_attributes(device_t dev, device_t child, struct dpaa2_cmd *cmd, + struct dpaa2_rc_attr *attr) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_RC_GET_ATTRIBUTES( + device_get_parent(dev), child, cmd, attr)); + return (ENXIO); + } + static int + bypass_rc_get_obj_region(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint32_t obj_id, uint8_t reg_idx, enum dpaa2_dev_type type, + struct dpaa2_rc_obj_region *reg) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_RC_GET_OBJ_REGION( + device_get_parent(dev), child, cmd, obj_id, reg_idx, + type, reg)); + return (ENXIO); + } + static int + bypass_rc_get_api_version(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint16_t *major, uint16_t *minor) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_RC_GET_API_VERSION( + device_get_parent(dev), child, cmd, major, minor)); + return (ENXIO); + } + static int + bypass_rc_set_irq_enable(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint8_t irq_idx, uint8_t enable) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_RC_SET_IRQ_ENABLE( + device_get_parent(dev), child, cmd, irq_idx, enable)); + return (ENXIO); + } + static int + bypass_rc_set_obj_irq(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint8_t irq_idx, uint64_t addr, uint32_t data, uint32_t irq_usr, + uint32_t obj_id, enum dpaa2_dev_type type) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_RC_SET_OBJ_IRQ( + device_get_parent(dev), child, cmd, irq_idx, addr, data, + irq_usr, obj_id, type)); + return (ENXIO); + } + static int + bypass_rc_get_conn(device_t dev, device_t child, struct dpaa2_cmd *cmd, + struct dpaa2_ep_desc *ep1_desc, struct dpaa2_ep_desc *ep2_desc, + uint32_t *link_stat) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_RC_GET_CONN( + device_get_parent(dev), child, cmd, ep1_desc, ep2_desc, + link_stat)); + return (ENXIO); + } + + static int + bypass_ni_open(device_t dev, device_t child, struct dpaa2_cmd *cmd, uint32_t dpni_id, + uint16_t *token) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_NI_OPEN( + device_get_parent(dev), child, cmd, dpni_id, token)); + return (ENXIO); + } + static int + bypass_ni_close(device_t dev, device_t child, struct dpaa2_cmd *cmd) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_NI_CLOSE( + device_get_parent(dev), child, cmd)); + return (ENXIO); + } + static int + bypass_ni_enable(device_t dev, device_t child, struct dpaa2_cmd *cmd) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_NI_ENABLE( + device_get_parent(dev), child, cmd)); + return (ENXIO); + } + static int + bypass_ni_disable(device_t dev, device_t child, struct dpaa2_cmd *cmd) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_NI_DISABLE( + device_get_parent(dev), child, cmd)); + return (ENXIO); + } + static int + bypass_ni_get_api_version(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint16_t *major, uint16_t *minor) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_NI_GET_API_VERSION( + device_get_parent(dev), child, cmd, major, minor)); + return (ENXIO); + } + static int + bypass_ni_reset(device_t dev, device_t child, struct dpaa2_cmd *cmd) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_NI_RESET( + device_get_parent(dev), child, cmd)); + return (ENXIO); + } + static int + bypass_ni_get_attributes(device_t dev, device_t child, struct dpaa2_cmd *cmd, + struct dpaa2_ni_attr *attr) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_NI_GET_ATTRIBUTES( + device_get_parent(dev), child, cmd, attr)); + return (ENXIO); + } + static int + bypass_ni_set_buf_layout(device_t dev, device_t child, struct dpaa2_cmd *cmd, + struct dpaa2_ni_buf_layout *bl) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_NI_SET_BUF_LAYOUT( + device_get_parent(dev), child, cmd, bl)); + return (ENXIO); + } + static int + bypass_ni_get_tx_data_off(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint16_t *offset) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_NI_GET_TX_DATA_OFF( + device_get_parent(dev), child, cmd, offset)); + return (ENXIO); + } + static int + bypass_ni_set_link_cfg(device_t dev, device_t child, struct dpaa2_cmd *cmd, + struct dpaa2_ni_link_cfg *cfg) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_NI_SET_LINK_CFG( + device_get_parent(dev), child, cmd, cfg)); + return (ENXIO); + } + static int + bypass_ni_get_link_cfg(device_t dev, device_t child, struct dpaa2_cmd *cmd, + struct dpaa2_ni_link_cfg *cfg) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_NI_GET_LINK_CFG( + device_get_parent(dev), child, cmd, cfg)); + return (ENXIO); + } + static int + bypass_ni_get_link_state(device_t dev, device_t child, struct dpaa2_cmd *cmd, + struct dpaa2_ni_link_state *state) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_NI_GET_LINK_STATE( + device_get_parent(dev), child, cmd, state)); + return (ENXIO); + } + static int + bypass_ni_get_port_mac_addr(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint8_t *mac) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_NI_GET_PORT_MAC_ADDR( + device_get_parent(dev), child, cmd, mac)); + return (ENXIO); + } + static int + bypass_ni_set_prim_mac_addr(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint8_t *mac) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_NI_SET_PRIM_MAC_ADDR( + device_get_parent(dev), child, cmd, mac)); + return (ENXIO); + } + static int + bypass_ni_get_prim_mac_addr(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint8_t *mac) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_NI_GET_PRIM_MAC_ADDR( + device_get_parent(dev), child, cmd, mac)); + return (ENXIO); + } + static int + bypass_ni_set_qos_table(device_t dev, device_t child, struct dpaa2_cmd *cmd, + struct dpaa2_ni_qos_table *tbl) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_NI_SET_QOS_TABLE( + device_get_parent(dev), child, cmd, tbl)); + return (ENXIO); + } + static int + bypass_ni_clear_qos_table(device_t dev, device_t child, struct dpaa2_cmd *cmd) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_NI_CLEAR_QOS_TABLE( + device_get_parent(dev), child, cmd)); + return (ENXIO); + } + static int + bypass_ni_set_pools(device_t dev, device_t child, struct dpaa2_cmd *cmd, + struct dpaa2_ni_pools_cfg *cfg) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_NI_SET_POOLS( + device_get_parent(dev), child, cmd, cfg)); + return (ENXIO); + } + static int + bypass_ni_set_err_behavior(device_t dev, device_t child, struct dpaa2_cmd *cmd, + struct dpaa2_ni_err_cfg *cfg) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_NI_SET_ERR_BEHAVIOR( + device_get_parent(dev), child, cmd, cfg)); + return (ENXIO); + } + static int + bypass_ni_get_queue(device_t dev, device_t child, struct dpaa2_cmd *cmd, + struct dpaa2_ni_queue_cfg *cfg) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_NI_GET_QUEUE( + device_get_parent(dev), child, cmd, cfg)); + return (ENXIO); + } + static int + bypass_ni_set_queue(device_t dev, device_t child, struct dpaa2_cmd *cmd, + struct dpaa2_ni_queue_cfg *cfg) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_NI_SET_QUEUE( + device_get_parent(dev), child, cmd, cfg)); + return (ENXIO); + } + static int + bypass_ni_get_qdid(device_t dev, device_t child, struct dpaa2_cmd *cmd, + enum dpaa2_ni_queue_type type, uint16_t *qdid) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_NI_GET_QDID( + device_get_parent(dev), child, cmd, type, qdid)); + return (ENXIO); + } + static int + bypass_ni_add_mac_addr(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint8_t *mac) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_NI_ADD_MAC_ADDR( + device_get_parent(dev), child, cmd, mac)); + return (ENXIO); + } + static int + bypass_ni_remove_mac_addr(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint8_t *mac) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_NI_REMOVE_MAC_ADDR( + device_get_parent(dev), child, cmd, mac)); + return (ENXIO); + } + static int + bypass_ni_clear_mac_filters(device_t dev, device_t child, struct dpaa2_cmd *cmd, + bool rm_uni, bool rm_multi) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_NI_CLEAR_MAC_FILTERS( + device_get_parent(dev), child, cmd, rm_uni, rm_multi)); + return (ENXIO); + } + static int + bypass_ni_set_mfl(device_t dev, device_t child, struct dpaa2_cmd *cmd, uint16_t length) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_NI_SET_MFL( + device_get_parent(dev), child, cmd, length)); + return (ENXIO); + } + static int + bypass_ni_set_offload(device_t dev, device_t child, struct dpaa2_cmd *cmd, + enum dpaa2_ni_ofl_type ofl_type, bool en) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_NI_SET_OFFLOAD( + device_get_parent(dev), child, cmd, ofl_type, en)); + return (ENXIO); + } + static int + bypass_ni_set_irq_mask(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint8_t irq_idx, uint32_t mask) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_NI_SET_IRQ_MASK( + device_get_parent(dev), child, cmd, irq_idx, mask)); + return (ENXIO); + } + static int + bypass_ni_set_irq_enable(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint8_t irq_idx, bool en) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_NI_SET_IRQ_ENABLE( + device_get_parent(dev), child, cmd, irq_idx, en)); + return (ENXIO); + } + static int + bypass_ni_get_irq_status(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint8_t irq_idx, uint32_t *status) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_NI_GET_IRQ_STATUS( + device_get_parent(dev), child, cmd, irq_idx, status)); + return (ENXIO); + } + static int + bypass_ni_set_uni_promisc(device_t dev, device_t child, struct dpaa2_cmd *cmd, bool en) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_NI_SET_UNI_PROMISC( + device_get_parent(dev), child, cmd, en)); + return (ENXIO); + } + static int + bypass_ni_set_multi_promisc(device_t dev, device_t child, struct dpaa2_cmd *cmd, bool en) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_NI_SET_MULTI_PROMISC( + device_get_parent(dev), child, cmd, en)); + return (ENXIO); + } + static int + bypass_ni_get_statistics(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint8_t page, uint16_t param, uint64_t *cnt) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_NI_GET_STATISTICS( + device_get_parent(dev), child, cmd, page, param, cnt)); + return (ENXIO); + } + static int + bypass_ni_set_rx_tc_dist(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint16_t dist_size, uint8_t tc, enum dpaa2_ni_dist_mode dist_mode, + bus_addr_t key_cfg_buf) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_NI_SET_RX_TC_DIST( + device_get_parent(dev), child, cmd, dist_size, tc, + dist_mode, key_cfg_buf)); + return (ENXIO); + } + + static int + bypass_io_open(device_t dev, device_t child, struct dpaa2_cmd *cmd, uint32_t dpio_id, + uint16_t *token) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_IO_OPEN( + device_get_parent(dev), child, cmd, dpio_id, token)); + return (ENXIO); + } + static int + bypass_io_close(device_t dev, device_t child, struct dpaa2_cmd *cmd) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_IO_CLOSE( + device_get_parent(dev), child, cmd)); + return (ENXIO); + } + static int + bypass_io_enable(device_t dev, device_t child, struct dpaa2_cmd *cmd) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_IO_ENABLE( + device_get_parent(dev), child, cmd)); + return (ENXIO); + } + static int + bypass_io_disable(device_t dev, device_t child, struct dpaa2_cmd *cmd) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_IO_DISABLE( + device_get_parent(dev), child, cmd)); + return (ENXIO); + } + static int + bypass_io_reset(device_t dev, device_t child, struct dpaa2_cmd *cmd) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_IO_RESET( + device_get_parent(dev), child, cmd)); + return (ENXIO); + } + static int + bypass_io_get_attributes(device_t dev, device_t child, struct dpaa2_cmd *cmd, + struct dpaa2_io_attr *attr) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_IO_GET_ATTRIBUTES( + device_get_parent(dev), child, cmd, attr)); + return (ENXIO); + } + static int + bypass_io_set_irq_mask(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint8_t irq_idx, uint32_t mask) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_IO_SET_IRQ_MASK( + device_get_parent(dev), child, cmd, irq_idx, mask)); + return (ENXIO); + } + static int + bypass_io_get_irq_status(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint8_t irq_idx, uint32_t *status) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_IO_GET_IRQ_STATUS( + device_get_parent(dev), child, cmd, irq_idx, status)); + return (ENXIO); + } + static int + bypass_io_set_irq_enable(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint8_t irq_idx, bool en) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_IO_SET_IRQ_ENABLE( + device_get_parent(dev), child, cmd, irq_idx, en)); + return (ENXIO); + } + static int + bypass_io_add_static_dq_chan(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint32_t dpcon_id, uint8_t *chan_idx) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_IO_ADD_STATIC_DQ_CHAN( + device_get_parent(dev), child, cmd, dpcon_id, chan_idx)); + return (ENXIO); + } + + static int + bypass_bp_open(device_t dev, device_t child, struct dpaa2_cmd *cmd, uint32_t dpbp_id, + uint16_t *token) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_BP_OPEN( + device_get_parent(dev), child, cmd, dpbp_id, token)); + return (ENXIO); + } + static int + bypass_bp_close(device_t dev, device_t child, struct dpaa2_cmd *cmd) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_BP_CLOSE( + device_get_parent(dev), child, cmd)); + return (ENXIO); + } + static int + bypass_bp_enable(device_t dev, device_t child, struct dpaa2_cmd *cmd) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_BP_ENABLE( + device_get_parent(dev), child, cmd)); + return (ENXIO); + } + static int + bypass_bp_disable(device_t dev, device_t child, struct dpaa2_cmd *cmd) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_BP_DISABLE( + device_get_parent(dev), child, cmd)); + return (ENXIO); + } + static int + bypass_bp_reset(device_t dev, device_t child, struct dpaa2_cmd *cmd) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_BP_RESET( + device_get_parent(dev), child, cmd)); + return (ENXIO); + } + static int + bypass_bp_get_attributes(device_t dev, device_t child, struct dpaa2_cmd *cmd, + struct dpaa2_bp_attr *attr) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_BP_GET_ATTRIBUTES( + device_get_parent(dev), child, cmd, attr)); + return (ENXIO); + } + + static int + bypass_mac_open(device_t dev, device_t child, struct dpaa2_cmd *cmd, uint32_t dpmac_id, + uint16_t *token) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_MAC_OPEN( + device_get_parent(dev), child, cmd, dpmac_id, token)); + return (ENXIO); + } + static int + bypass_mac_close(device_t dev, device_t child, struct dpaa2_cmd *cmd) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_MAC_CLOSE( + device_get_parent(dev), child, cmd)); + return (ENXIO); + } + static int + bypass_mac_reset(device_t dev, device_t child, struct dpaa2_cmd *cmd) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_MAC_RESET( + device_get_parent(dev), child, cmd)); + return (ENXIO); + } + static int + bypass_mac_mdio_read(device_t dev, device_t child, struct dpaa2_cmd *cmd, uint8_t phy, + uint16_t reg, uint16_t *val) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_MAC_MDIO_READ( + device_get_parent(dev), child, cmd, phy, reg, val)); + return (ENXIO); + } + static int + bypass_mac_mdio_write(device_t dev, device_t child, struct dpaa2_cmd *cmd, uint8_t phy, + uint16_t reg, uint16_t val) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_MAC_MDIO_WRITE( + device_get_parent(dev), child, cmd, phy, reg, val)); + return (ENXIO); + } + static int + bypass_mac_get_addr(device_t dev, device_t child, struct dpaa2_cmd *cmd, uint8_t *mac) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_MAC_GET_ADDR( + device_get_parent(dev), child, cmd, mac)); + return (ENXIO); + } + static int + bypass_mac_get_attributes(device_t dev, device_t child, struct dpaa2_cmd *cmd, + struct dpaa2_mac_attr *attr) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_MAC_GET_ATTRIBUTES( + device_get_parent(dev), child, cmd, attr)); + return (ENXIO); + } + static int + bypass_mac_set_link_state(device_t dev, device_t child, struct dpaa2_cmd *cmd, + struct dpaa2_mac_link_state *state) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_MAC_SET_LINK_STATE( + device_get_parent(dev), child, cmd, state)); + return (ENXIO); + } + static int + bypass_mac_set_irq_mask(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint8_t irq_idx, uint32_t mask) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_MAC_SET_IRQ_MASK( + device_get_parent(dev), child, cmd, irq_idx, mask)); + return (ENXIO); + } + static int + bypass_mac_set_irq_enable(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint8_t irq_idx, bool en) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_MAC_SET_IRQ_ENABLE( + device_get_parent(dev), child, cmd, irq_idx, en)); + return (ENXIO); + } + static int + bypass_mac_get_irq_status(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint8_t irq_idx, uint32_t *status) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_MAC_GET_IRQ_STATUS( + device_get_parent(dev), child, cmd, irq_idx, status)); + return (ENXIO); + } + + static int + bypass_con_open(device_t dev, device_t child, struct dpaa2_cmd *cmd, uint32_t dpcon_id, + uint16_t *token) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_CON_OPEN( + device_get_parent(dev), child, cmd, dpcon_id, token)); + return (ENXIO); + } + static int + bypass_con_close(device_t dev, device_t child, struct dpaa2_cmd *cmd) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_CON_CLOSE( + device_get_parent(dev), child, cmd)); + return (ENXIO); + } + static int + bypass_con_reset(device_t dev, device_t child, struct dpaa2_cmd *cmd) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_CON_RESET( + device_get_parent(dev), child, cmd)); + return (ENXIO); + } + static int + bypass_con_enable(device_t dev, device_t child, struct dpaa2_cmd *cmd) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_CON_ENABLE( + device_get_parent(dev), child, cmd)); + return (ENXIO); + } + static int + bypass_con_disable(device_t dev, device_t child, struct dpaa2_cmd *cmd) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_CON_DISABLE( + device_get_parent(dev), child, cmd)); + return (ENXIO); + } + static int + bypass_con_get_attributes(device_t dev, device_t child, struct dpaa2_cmd *cmd, + struct dpaa2_con_attr *attr) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_CON_GET_ATTRIBUTES( + device_get_parent(dev), child, cmd, attr)); + return (ENXIO); + } + static int + bypass_con_set_notif(device_t dev, device_t child, struct dpaa2_cmd *cmd, + struct dpaa2_con_notif_cfg *cfg) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_CON_SET_NOTIF( + device_get_parent(dev), child, cmd, cfg)); + return (ENXIO); + } + + /* Data Path MC Portal (DPMCP) commands. */ + + static int + bypass_mcp_create(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint32_t portal_id, uint32_t options, uint32_t *dpmcp_id) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_MCP_CREATE( + device_get_parent(dev), child, cmd, portal_id, + options, dpmcp_id)); + return (ENXIO); + } + static int + bypass_mcp_destroy(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint32_t dpmcp_id) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_MCP_DESTROY( + device_get_parent(dev), child, cmd, dpmcp_id)); + return (ENXIO); + } + static int + bypass_mcp_open(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint32_t dpmcp_id, uint16_t *token) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_MCP_OPEN( + device_get_parent(dev), child, cmd, dpmcp_id, + token)); + return (ENXIO); + } + static int + bypass_mcp_close(device_t dev, device_t child, struct dpaa2_cmd *cmd) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_MCP_CLOSE( + device_get_parent(dev), child, cmd)); + return (ENXIO); + } + static int + bypass_mcp_reset(device_t dev, device_t child, struct dpaa2_cmd *cmd) + { + panic_on_mc(dev); + if (device_get_parent(dev) != NULL) + return (DPAA2_CMD_MCP_RESET( + device_get_parent(dev), child, cmd)); + return (ENXIO); + } +}; + +/** + * @brief Data Path Management (DPMNG) commands. + */ + +METHOD int mng_get_version { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + uint32_t *major; + uint32_t *minor; + uint32_t *rev; +} DEFAULT bypass_mng_get_version; + +METHOD int mng_get_soc_version { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + uint32_t *pvr; + uint32_t *svr; +} DEFAULT bypass_mng_get_soc_version; + +METHOD int mng_get_container_id { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + uint32_t *cont_id; +} DEFAULT bypass_mng_get_container_id; + +/** + * @brief Data Path Resource Containter (DPRC) commands. + */ + +METHOD int rc_open { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + uint32_t cont_id; + uint16_t *token; +} DEFAULT bypass_rc_open; + +METHOD int rc_close { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; +} DEFAULT bypass_rc_close; + +METHOD int rc_get_obj_count { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + uint32_t *obj_count; +} DEFAULT bypass_rc_get_obj_count; + +METHOD int rc_get_obj { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + uint32_t obj_idx; + struct dpaa2_obj *obj; +} DEFAULT bypass_rc_get_obj; + +METHOD int rc_get_obj_descriptor { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + uint32_t obj_id; + enum dpaa2_dev_type type; + struct dpaa2_obj *obj; +} DEFAULT bypass_rc_get_obj_descriptor; + +METHOD int rc_get_attributes { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + struct dpaa2_rc_attr *attr; +} DEFAULT bypass_rc_get_attributes; + +METHOD int rc_get_obj_region { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + uint32_t obj_id; + uint8_t reg_idx; + enum dpaa2_dev_type type; + struct dpaa2_rc_obj_region *reg; +} DEFAULT bypass_rc_get_obj_region; + +METHOD int rc_get_api_version { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + uint16_t *major; + uint16_t *minor; +} DEFAULT bypass_rc_get_api_version; + +METHOD int rc_set_irq_enable { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + uint8_t irq_idx; + uint8_t enable; +} DEFAULT bypass_rc_set_irq_enable; + +METHOD int rc_set_obj_irq { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + uint8_t irq_idx; + uint64_t addr; + uint32_t data; + uint32_t irq_usr; + uint32_t obj_id; + enum dpaa2_dev_type type; +} DEFAULT bypass_rc_set_obj_irq; + +METHOD int rc_get_conn { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + struct dpaa2_ep_desc *ep1_desc; + struct dpaa2_ep_desc *ep2_desc; + uint32_t *link_stat; +} DEFAULT bypass_rc_get_conn; + +/** + * @brief Data Path Network Interface (DPNI) commands. + */ + +METHOD int ni_open { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + uint32_t dpni_id; + uint16_t *token; +} DEFAULT bypass_ni_open; + +METHOD int ni_close { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; +} DEFAULT bypass_ni_close; + +METHOD int ni_enable { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; +} DEFAULT bypass_ni_enable; + +METHOD int ni_disable { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; +} DEFAULT bypass_ni_disable; + +METHOD int ni_get_api_version { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + uint16_t *major; + uint16_t *minor; +} DEFAULT bypass_ni_get_api_version; + +METHOD int ni_reset { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; +} DEFAULT bypass_ni_reset; + +METHOD int ni_get_attributes { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + struct dpaa2_ni_attr *attr; +} DEFAULT bypass_ni_get_attributes; + +METHOD int ni_set_buf_layout { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + struct dpaa2_ni_buf_layout *bl; +} DEFAULT bypass_ni_set_buf_layout; + +METHOD int ni_get_tx_data_off { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + uint16_t *offset; +} DEFAULT bypass_ni_get_tx_data_off; + +METHOD int ni_set_link_cfg { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + struct dpaa2_ni_link_cfg *cfg; +} DEFAULT bypass_ni_set_link_cfg; + +METHOD int ni_get_link_cfg { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + struct dpaa2_ni_link_cfg *cfg; +} DEFAULT bypass_ni_get_link_cfg; + +METHOD int ni_get_link_state { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + struct dpaa2_ni_link_state *state; +} DEFAULT bypass_ni_get_link_state; + +METHOD int ni_get_port_mac_addr { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + uint8_t *mac; +} DEFAULT bypass_ni_get_port_mac_addr; + +METHOD int ni_set_prim_mac_addr { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + uint8_t *mac; +} DEFAULT bypass_ni_set_prim_mac_addr; + +METHOD int ni_get_prim_mac_addr { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + uint8_t *mac; +} DEFAULT bypass_ni_get_prim_mac_addr; + +METHOD int ni_set_qos_table { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + struct dpaa2_ni_qos_table *tbl; +} DEFAULT bypass_ni_set_qos_table; + +METHOD int ni_clear_qos_table { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; +} DEFAULT bypass_ni_clear_qos_table; + +METHOD int ni_set_pools { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + struct dpaa2_ni_pools_cfg *cfg; +} DEFAULT bypass_ni_set_pools; + +METHOD int ni_set_err_behavior { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + struct dpaa2_ni_err_cfg *cfg; +} DEFAULT bypass_ni_set_err_behavior; + +METHOD int ni_get_queue { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + struct dpaa2_ni_queue_cfg *cfg; +} DEFAULT bypass_ni_get_queue; + +METHOD int ni_set_queue { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + struct dpaa2_ni_queue_cfg *cfg; +} DEFAULT bypass_ni_set_queue; + +METHOD int ni_get_qdid { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + enum dpaa2_ni_queue_type type; + uint16_t *qdid; +} DEFAULT bypass_ni_get_qdid; + +METHOD int ni_add_mac_addr { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + uint8_t *mac; +} DEFAULT bypass_ni_add_mac_addr; + +METHOD int ni_remove_mac_addr { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + uint8_t *mac; +} DEFAULT bypass_ni_remove_mac_addr; + +METHOD int ni_clear_mac_filters { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + bool rm_uni; + bool rm_multi; +} DEFAULT bypass_ni_clear_mac_filters; + +METHOD int ni_set_mfl { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + uint16_t length; +} DEFAULT bypass_ni_set_mfl; + +METHOD int ni_set_offload { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + enum dpaa2_ni_ofl_type ofl_type; + bool en; +} DEFAULT bypass_ni_set_offload; + +METHOD int ni_set_irq_mask { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + uint8_t irq_idx; + uint32_t mask; +} DEFAULT bypass_ni_set_irq_mask; + +METHOD int ni_set_irq_enable { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + uint8_t irq_idx; + bool en; +} DEFAULT bypass_ni_set_irq_enable; + +METHOD int ni_get_irq_status { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + uint8_t irq_idx; + uint32_t *status; +} DEFAULT bypass_ni_get_irq_status; + +METHOD int ni_set_uni_promisc { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + bool en; +} DEFAULT bypass_ni_set_uni_promisc; + +METHOD int ni_set_multi_promisc { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + bool en; +} DEFAULT bypass_ni_set_multi_promisc; + +METHOD int ni_get_statistics { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + uint8_t page; + uint16_t param; + uint64_t *cnt; +} DEFAULT bypass_ni_get_statistics; + +METHOD int ni_set_rx_tc_dist { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + uint16_t dist_size; + uint8_t tc; + enum dpaa2_ni_dist_mode dist_mode; + bus_addr_t key_cfg_buf; +} DEFAULT bypass_ni_set_rx_tc_dist; + +/** + * @brief Data Path I/O (DPIO) commands. + */ + +METHOD int io_open { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + uint32_t dpio_id; + uint16_t *token; +} DEFAULT bypass_io_open; + +METHOD int io_close { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; +} DEFAULT bypass_io_close; + +METHOD int io_enable { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; +} DEFAULT bypass_io_enable; + +METHOD int io_disable { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; +} DEFAULT bypass_io_disable; + +METHOD int io_reset { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; +} DEFAULT bypass_io_reset; + +METHOD int io_get_attributes { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + struct dpaa2_io_attr *attr; +} DEFAULT bypass_io_get_attributes; + +METHOD int io_set_irq_mask { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + uint8_t irq_idx; + uint32_t mask; +} DEFAULT bypass_io_set_irq_mask; + +METHOD int io_get_irq_status { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + uint8_t irq_idx; + uint32_t *status; +} DEFAULT bypass_io_get_irq_status; + +METHOD int io_set_irq_enable { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + uint8_t irq_idx; + bool en; +} DEFAULT bypass_io_set_irq_enable; + +METHOD int io_add_static_dq_chan { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + uint32_t dpcon_id; + uint8_t *chan_idx; +} DEFAULT bypass_io_add_static_dq_chan; + +/** + * @brief Data Path Buffer Pool (DPBP) commands. + */ + +METHOD int bp_open { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + uint32_t dpbp_id; + uint16_t *token; +} DEFAULT bypass_bp_open; + +METHOD int bp_close { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; +} DEFAULT bypass_bp_close; + +METHOD int bp_enable { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; +} DEFAULT bypass_bp_enable; + +METHOD int bp_disable { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; +} DEFAULT bypass_bp_disable; + +METHOD int bp_reset { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; +} DEFAULT bypass_bp_reset; + +METHOD int bp_get_attributes { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + struct dpaa2_bp_attr *attr; +} DEFAULT bypass_bp_get_attributes; + +/** + * @brief Data Path MAC (DPMAC) commands. + */ + +METHOD int mac_open { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + uint32_t dpmac_id; + uint16_t *token; +} DEFAULT bypass_mac_open; + +METHOD int mac_close { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; +} DEFAULT bypass_mac_close; + +METHOD int mac_reset { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; +} DEFAULT bypass_mac_reset; + +METHOD int mac_mdio_read { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + uint8_t phy; + uint16_t reg; + uint16_t *val; +} DEFAULT bypass_mac_mdio_read; + +METHOD int mac_mdio_write { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + uint8_t phy; + uint16_t reg; + uint16_t val; +} DEFAULT bypass_mac_mdio_write; + +METHOD int mac_get_addr { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + uint8_t *mac; +} DEFAULT bypass_mac_get_addr; + +METHOD int mac_get_attributes { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + struct dpaa2_mac_attr *attr; +} DEFAULT bypass_mac_get_attributes; + +METHOD int mac_set_link_state { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + struct dpaa2_mac_link_state *state; +} DEFAULT bypass_mac_set_link_state; + +METHOD int mac_set_irq_mask { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + uint8_t irq_idx; + uint32_t mask; +} DEFAULT bypass_mac_set_irq_mask; + +METHOD int mac_set_irq_enable { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + uint8_t irq_idx; + bool en; +} DEFAULT bypass_mac_set_irq_enable; + +METHOD int mac_get_irq_status { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + uint8_t irq_idx; + uint32_t *status; +} DEFAULT bypass_mac_get_irq_status; + +/** + * @brief Data Path Concentrator (DPCON) commands. + */ + +METHOD int con_open { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + uint32_t dpcon_id; + uint16_t *token; +} DEFAULT bypass_con_open; + +METHOD int con_close { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; +} DEFAULT bypass_con_close; + +METHOD int con_reset { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; +} DEFAULT bypass_con_reset; + +METHOD int con_enable { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; +} DEFAULT bypass_con_enable; + +METHOD int con_disable { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; +} DEFAULT bypass_con_disable; + +METHOD int con_get_attributes { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + struct dpaa2_con_attr *attr; +} DEFAULT bypass_con_get_attributes; + +METHOD int con_set_notif { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + struct dpaa2_con_notif_cfg *cfg; +} DEFAULT bypass_con_set_notif; + +/** + * @brief Data Path MC Portal (DPMCP) commands. + */ + +METHOD int mcp_create { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + uint32_t portal_id; + uint32_t options; + uint32_t *dpmcp_id; +} DEFAULT bypass_mcp_create; + +METHOD int mcp_destroy { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + uint32_t dpmcp_id; +} DEFAULT bypass_mcp_destroy; + +METHOD int mcp_open { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; + uint32_t dpmcp_id; + uint16_t *token; +} DEFAULT bypass_mcp_open; + +METHOD int mcp_close { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; +} DEFAULT bypass_mcp_close; + +METHOD int mcp_reset { + device_t dev; + device_t child; + struct dpaa2_cmd *cmd; +} DEFAULT bypass_mcp_reset; diff --git a/sys/dev/dpaa2/dpaa2_con.c b/sys/dev/dpaa2/dpaa2_con.c new file mode 100644 index 000000000000..602497c2c8de --- /dev/null +++ b/sys/dev/dpaa2/dpaa2_con.c @@ -0,0 +1,213 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright © 2021-2022 Dmitry Salychev + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +/* + * The DPAA2 Concentrator (DPCON) driver. + * + * Supports configuration of QBMan channels for advanced scheduling of ingress + * packets from one or more network interfaces. + * + * DPCONs are used to distribute Rx or Tx Confirmation traffic to different + * cores, via affine DPIO objects. The implication is that one DPCON must be + * available for each core where Rx or Tx Confirmation traffic should be + * distributed to. + * + * QBMan channel contains several work queues. The WQs within a channel have a + * priority relative to each other. Each channel consists of either eight or two + * WQs, and thus, there are either eight or two possible priorities in a channel. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#include "pcib_if.h" +#include "pci_if.h" + +#include "dpaa2_mcp.h" +#include "dpaa2_swp.h" +#include "dpaa2_mc.h" +#include "dpaa2_cmd_if.h" + +/* DPAA2 Concentrator resource specification. */ +struct resource_spec dpaa2_con_spec[] = { + /* + * DPMCP resources. + * + * NOTE: MC command portals (MCPs) are used to send commands to, and + * receive responses from, the MC firmware. One portal per DPCON. + */ +#define MCP_RES_NUM (1u) +#define MCP_RID_OFF (0u) +#define MCP_RID(rid) ((rid) + MCP_RID_OFF) + /* --- */ + { DPAA2_DEV_MCP, MCP_RID(0), RF_ACTIVE | RF_SHAREABLE | RF_OPTIONAL }, + /* --- */ + RESOURCE_SPEC_END +}; + +static int dpaa2_con_detach(device_t dev); + +/* + * Device interface. + */ + +static int +dpaa2_con_probe(device_t dev) +{ + /* DPCON device will be added by a parent resource container itself. */ + device_set_desc(dev, "DPAA2 Concentrator"); + return (BUS_PROBE_DEFAULT); +} + +static int +dpaa2_con_detach(device_t dev) +{ + device_t child = dev; + struct dpaa2_con_softc *sc = device_get_softc(dev); + + DPAA2_CMD_CON_CLOSE(dev, child, dpaa2_mcp_tk(sc->cmd, sc->con_token)); + DPAA2_CMD_RC_CLOSE(dev, child, dpaa2_mcp_tk(sc->cmd, sc->rc_token)); + dpaa2_mcp_free_command(sc->cmd); + + sc->cmd = NULL; + sc->con_token = 0; + sc->rc_token = 0; + + return (0); +} + +static int +dpaa2_con_attach(device_t dev) +{ + device_t pdev = device_get_parent(dev); + device_t child = dev; + device_t mcp_dev; + struct dpaa2_con_softc *sc = device_get_softc(dev); + struct dpaa2_devinfo *rcinfo = device_get_ivars(pdev); + struct dpaa2_devinfo *dinfo = device_get_ivars(dev); + struct dpaa2_devinfo *mcp_dinfo; + int error; + + sc->dev = dev; + + error = bus_alloc_resources(sc->dev, dpaa2_con_spec, sc->res); + if (error) { + device_printf(dev, "%s: failed to allocate resources: " + "error=%d\n", __func__, error); + return (ENXIO); + } + + /* Obtain MC portal. */ + mcp_dev = (device_t) rman_get_start(sc->res[MCP_RID(0)]); + mcp_dinfo = device_get_ivars(mcp_dev); + dinfo->portal = mcp_dinfo->portal; + + /* Allocate a command to send to MC hardware. */ + error = dpaa2_mcp_init_command(&sc->cmd, DPAA2_CMD_DEF); + if (error) { + device_printf(dev, "Failed to allocate dpaa2_cmd: error=%d\n", + error); + goto err_exit; + } + + /* Open resource container and DPCON object. */ + error = DPAA2_CMD_RC_OPEN(dev, child, sc->cmd, rcinfo->id, + &sc->rc_token); + if (error) { + device_printf(dev, "Failed to open DPRC: error=%d\n", error); + goto err_free_cmd; + } + error = DPAA2_CMD_CON_OPEN(dev, child, sc->cmd, dinfo->id, + &sc->con_token); + if (error) { + device_printf(dev, "Failed to open DPCON: id=%d, error=%d\n", + dinfo->id, error); + goto err_close_rc; + } + + /* Prepare DPCON object. */ + error = DPAA2_CMD_CON_RESET(dev, child, sc->cmd); + if (error) { + device_printf(dev, "Failed to reset DPCON: id=%d, error=%d\n", + dinfo->id, error); + goto err_close_con; + } + error = DPAA2_CMD_CON_GET_ATTRIBUTES(dev, child, sc->cmd, &sc->attr); + if (error) { + device_printf(dev, "Failed to get DPCON attributes: id=%d, " + "error=%d\n", dinfo->id, error); + goto err_close_con; + } + + /* TODO: Enable debug output via sysctl (to reduce output). */ + if (bootverbose) + device_printf(dev, "chan_id=%d, priorities=%d\n", + sc->attr.chan_id, sc->attr.prior_num); + + return (0); + + err_close_con: + DPAA2_CMD_CON_CLOSE(dev, child, dpaa2_mcp_tk(sc->cmd, sc->con_token)); + err_close_rc: + DPAA2_CMD_RC_CLOSE(dev, child, dpaa2_mcp_tk(sc->cmd, sc->rc_token)); + err_free_cmd: + dpaa2_mcp_free_command(sc->cmd); + err_exit: + return (ENXIO); +} + +static device_method_t dpaa2_con_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, dpaa2_con_probe), + DEVMETHOD(device_attach, dpaa2_con_attach), + DEVMETHOD(device_detach, dpaa2_con_detach), + + DEVMETHOD_END +}; + +static driver_t dpaa2_con_driver = { + "dpaa2_con", + dpaa2_con_methods, + sizeof(struct dpaa2_con_softc), +}; + +DRIVER_MODULE(dpaa2_con, dpaa2_rc, dpaa2_con_driver, 0, 0); diff --git a/sys/dev/dpaa2/dpaa2_con.h b/sys/dev/dpaa2/dpaa2_con.h new file mode 100644 index 000000000000..82fd50f4eaed --- /dev/null +++ b/sys/dev/dpaa2/dpaa2_con.h @@ -0,0 +1,70 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright © 2021-2022 Dmitry Salychev + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _DPAA2_CON_H +#define _DPAA2_CON_H + +#include +#include +#include + +#include "dpaa2_types.h" +#include "dpaa2_mcp.h" + +/* Maximum resources per DPCON: 1 DPMCP. */ +#define DPAA2_CON_MAX_RESOURCES 1 + +/** + * @brief Attributes of the DPCON object. + * + * id: DPCON object ID. + * chan_id: QBMan channel ID to be used for dequeue operations. + * prior_num: Number of priorities for the DPCON channel (1-8). + */ +struct dpaa2_con_attr { + uint32_t id; + uint16_t chan_id; + uint8_t prior_num; +}; + +/** + * @brief Software context for the DPAA2 Concentrator driver. + */ +struct dpaa2_con_softc { + device_t dev; + struct resource *res[DPAA2_CON_MAX_RESOURCES]; + struct dpaa2_con_attr attr; + + /* Help to send commands to MC. */ + struct dpaa2_cmd *cmd; + uint16_t rc_token; + uint16_t con_token; +}; + +extern struct resource_spec dpaa2_con_spec[]; + +#endif /* _DPAA2_CON_H */ diff --git a/sys/dev/dpaa2/dpaa2_io.c b/sys/dev/dpaa2/dpaa2_io.c new file mode 100644 index 000000000000..e2b7992bfdb6 --- /dev/null +++ b/sys/dev/dpaa2/dpaa2_io.c @@ -0,0 +1,570 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright © 2021-2022 Dmitry Salychev + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +/* + * QBMan command interface and the DPAA2 I/O (DPIO) driver. + * + * The DPIO object allows configuration of the QBMan software portal with + * optional notification capabilities. + * + * Software portals are used by the driver to communicate with the QBMan. The + * DPIO object’s main purpose is to enable the driver to perform I/O – enqueue + * and dequeue operations, as well as buffer release and acquire operations – + * using QBMan. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#include "pcib_if.h" +#include "pci_if.h" + +#include "dpaa2_mc.h" +#include "dpaa2_mcp.h" +#include "dpaa2_swp.h" +#include "dpaa2_swp_if.h" +#include "dpaa2_cmd_if.h" +#include "dpaa2_io.h" +#include "dpaa2_ni.h" + +#define DPIO_IRQ_INDEX 0 /* index of the only DPIO IRQ */ +#define DPIO_POLL_MAX 32 + +/* + * Memory: + * 0: cache-enabled part of the QBMan software portal. + * 1: cache-inhibited part of the QBMan software portal. + * 2: control registers of the QBMan software portal? + * + * Note that MSI should be allocated separately using pseudo-PCI interface. + */ +struct resource_spec dpaa2_io_spec[] = { + /* + * System Memory resources. + */ +#define MEM_RES_NUM (3u) +#define MEM_RID_OFF (0u) +#define MEM_RID(rid) ((rid) + MEM_RID_OFF) + { SYS_RES_MEMORY, MEM_RID(0), RF_ACTIVE | RF_UNMAPPED }, + { SYS_RES_MEMORY, MEM_RID(1), RF_ACTIVE | RF_UNMAPPED }, + { SYS_RES_MEMORY, MEM_RID(2), RF_ACTIVE | RF_UNMAPPED | RF_OPTIONAL }, + /* + * DPMCP resources. + * + * NOTE: MC command portals (MCPs) are used to send commands to, and + * receive responses from, the MC firmware. One portal per DPIO. + */ +#define MCP_RES_NUM (1u) +#define MCP_RID_OFF (MEM_RID_OFF + MEM_RES_NUM) +#define MCP_RID(rid) ((rid) + MCP_RID_OFF) + /* --- */ + { DPAA2_DEV_MCP, MCP_RID(0), RF_ACTIVE | RF_SHAREABLE | RF_OPTIONAL }, + /* --- */ + RESOURCE_SPEC_END +}; + +/* Configuration routines. */ +static int dpaa2_io_setup_irqs(device_t dev); +static int dpaa2_io_release_irqs(device_t dev); +static int dpaa2_io_setup_msi(struct dpaa2_io_softc *sc); +static int dpaa2_io_release_msi(struct dpaa2_io_softc *sc); + +/* Interrupt handlers */ +static void dpaa2_io_intr(void *arg); + +static int +dpaa2_io_probe(device_t dev) +{ + /* DPIO device will be added by a parent resource container itself. */ + device_set_desc(dev, "DPAA2 I/O"); + return (BUS_PROBE_DEFAULT); +} + +static int +dpaa2_io_detach(device_t dev) +{ + device_t child = dev; + struct dpaa2_io_softc *sc = device_get_softc(dev); + struct dpaa2_devinfo *dinfo = device_get_ivars(dev); + int error; + + /* Tear down interrupt handler and release IRQ resources. */ + dpaa2_io_release_irqs(dev); + + /* Free software portal helper object. */ + dpaa2_swp_free_portal(sc->swp); + + /* Disable DPIO object. */ + error = DPAA2_CMD_IO_DISABLE(dev, child, dpaa2_mcp_tk(sc->cmd, + sc->io_token)); + if (error && bootverbose) + device_printf(dev, "%s: failed to disable DPIO: id=%d, " + "error=%d\n", __func__, dinfo->id, error); + + /* Close control sessions with the DPAA2 objects. */ + DPAA2_CMD_IO_CLOSE(dev, child, dpaa2_mcp_tk(sc->cmd, sc->io_token)); + DPAA2_CMD_RC_CLOSE(dev, child, dpaa2_mcp_tk(sc->cmd, sc->rc_token)); + + /* Free pre-allocated MC command. */ + dpaa2_mcp_free_command(sc->cmd); + sc->cmd = NULL; + sc->io_token = 0; + sc->rc_token = 0; + + /* Unmap memory resources of the portal. */ + for (int i = 0; i < MEM_RES_NUM; i++) { + if (sc->res[MEM_RID(i)] == NULL) + continue; + error = bus_unmap_resource(sc->dev, SYS_RES_MEMORY, + sc->res[MEM_RID(i)], &sc->map[MEM_RID(i)]); + if (error && bootverbose) + device_printf(dev, "%s: failed to unmap memory " + "resource: rid=%d, error=%d\n", __func__, MEM_RID(i), + error); + } + + /* Release allocated resources. */ + bus_release_resources(dev, dpaa2_io_spec, sc->res); + + return (0); +} + +static int +dpaa2_io_attach(device_t dev) +{ + device_t pdev = device_get_parent(dev); + device_t child = dev; + device_t mcp_dev; + struct dpaa2_io_softc *sc = device_get_softc(dev); + struct dpaa2_devinfo *rcinfo = device_get_ivars(pdev); + struct dpaa2_devinfo *dinfo = device_get_ivars(dev); + struct dpaa2_devinfo *mcp_dinfo; + struct resource_map_request req; + struct { + vm_memattr_t memattr; + char *label; + } map_args[MEM_RES_NUM] = { + { VM_MEMATTR_WRITE_BACK, "cache-enabled part" }, + { VM_MEMATTR_DEVICE, "cache-inhibited part" }, + { VM_MEMATTR_DEVICE, "control registers" } + }; + int error; + + sc->dev = dev; + sc->swp = NULL; + sc->cmd = NULL; + sc->intr = NULL; + sc->irq_resource = NULL; + + /* Allocate resources. */ + error = bus_alloc_resources(sc->dev, dpaa2_io_spec, sc->res); + if (error) { + device_printf(dev, "%s: failed to allocate resources: " + "error=%d\n", __func__, error); + return (ENXIO); + } + + /* Set allocated MC portal up. */ + mcp_dev = (device_t) rman_get_start(sc->res[MCP_RID(0)]); + mcp_dinfo = device_get_ivars(mcp_dev); + dinfo->portal = mcp_dinfo->portal; + + /* Map memory resources of the portal. */ + for (int i = 0; i < MEM_RES_NUM; i++) { + if (sc->res[MEM_RID(i)] == NULL) + continue; + + resource_init_map_request(&req); + req.memattr = map_args[i].memattr; + error = bus_map_resource(sc->dev, SYS_RES_MEMORY, + sc->res[MEM_RID(i)], &req, &sc->map[MEM_RID(i)]); + if (error) { + device_printf(dev, "%s: failed to map %s: error=%d\n", + __func__, map_args[i].label, error); + goto err_exit; + } + } + + /* Allocate a command to send to the MC hardware. */ + error = dpaa2_mcp_init_command(&sc->cmd, DPAA2_CMD_DEF); + if (error) { + device_printf(dev, "%s: failed to allocate dpaa2_cmd: " + "error=%d\n", __func__, error); + goto err_exit; + } + + /* Prepare DPIO object. */ + error = DPAA2_CMD_RC_OPEN(dev, child, sc->cmd, rcinfo->id, + &sc->rc_token); + if (error) { + device_printf(dev, "%s: failed to open DPRC: error=%d\n", + __func__, error); + goto err_exit; + } + error = DPAA2_CMD_IO_OPEN(dev, child, sc->cmd, dinfo->id, &sc->io_token); + if (error) { + device_printf(dev, "%s: failed to open DPIO: id=%d, error=%d\n", + __func__, dinfo->id, error); + goto err_exit; + } + error = DPAA2_CMD_IO_RESET(dev, child, sc->cmd); + if (error) { + device_printf(dev, "%s: failed to reset DPIO: id=%d, error=%d\n", + __func__, dinfo->id, error); + goto err_exit; + } + error = DPAA2_CMD_IO_GET_ATTRIBUTES(dev, child, sc->cmd, &sc->attr); + if (error) { + device_printf(dev, "%s: failed to get DPIO attributes: id=%d, " + "error=%d\n", __func__, dinfo->id, error); + goto err_exit; + } + error = DPAA2_CMD_IO_ENABLE(dev, child, sc->cmd); + if (error) { + device_printf(dev, "%s: failed to enable DPIO: id=%d, " + "error=%d\n", __func__, dinfo->id, error); + goto err_exit; + } + + /* Prepare descriptor of the QBMan software portal. */ + sc->swp_desc.dpio_dev = dev; + sc->swp_desc.swp_version = sc->attr.swp_version; + sc->swp_desc.swp_clk = sc->attr.swp_clk; + sc->swp_desc.swp_id = sc->attr.swp_id; + sc->swp_desc.has_notif = sc->attr.priors_num ? true : false; + sc->swp_desc.has_8prio = sc->attr.priors_num == 8u ? true : false; + + sc->swp_desc.cena_res = sc->res[0]; + sc->swp_desc.cena_map = &sc->map[0]; + sc->swp_desc.cinh_res = sc->res[1]; + sc->swp_desc.cinh_map = &sc->map[1]; + + /* + * Compute how many 256 QBMAN cycles fit into one ns. This is because + * the interrupt timeout period register needs to be specified in QBMAN + * clock cycles in increments of 256. + */ + sc->swp_desc.swp_cycles_ratio = 256000 / + (sc->swp_desc.swp_clk / 1000000); + + /* Initialize QBMan software portal. */ + error = dpaa2_swp_init_portal(&sc->swp, &sc->swp_desc, DPAA2_SWP_DEF); + if (error) { + device_printf(dev, "%s: failed to initialize dpaa2_swp: " + "error=%d\n", __func__, error); + goto err_exit; + } + + error = dpaa2_io_setup_irqs(dev); + if (error) { + device_printf(dev, "%s: failed to setup IRQs: error=%d\n", + __func__, error); + goto err_exit; + } + +#if 0 + /* TODO: Enable debug output via sysctl (to reduce output). */ + if (bootverbose) + device_printf(dev, "dpio_id=%d, swp_id=%d, chan_mode=%s, " + "notif_priors=%d, swp_version=0x%x\n", + sc->attr.id, sc->attr.swp_id, + sc->attr.chan_mode == DPAA2_IO_LOCAL_CHANNEL + ? "local_channel" : "no_channel", sc->attr.priors_num, + sc->attr.swp_version); +#endif + return (0); + +err_exit: + dpaa2_io_detach(dev); + return (ENXIO); +} + +/** + * @brief Enqueue multiple frames to a frame queue using one FQID. + */ +static int +dpaa2_io_enq_multiple_fq(device_t iodev, uint32_t fqid, + struct dpaa2_fd *fd, int frames_n) +{ + struct dpaa2_io_softc *sc = device_get_softc(iodev); + struct dpaa2_swp *swp = sc->swp; + struct dpaa2_eq_desc ed; + uint32_t flags = 0; + + memset(&ed, 0, sizeof(ed)); + + /* Setup enqueue descriptor. */ + dpaa2_swp_set_ed_norp(&ed, false); + dpaa2_swp_set_ed_fq(&ed, fqid); + + return (dpaa2_swp_enq_mult(swp, &ed, fd, &flags, frames_n)); +} + +/** + * @brief Configure the channel data availability notification (CDAN) + * in a particular WQ channel paired with DPIO. + */ +static int +dpaa2_io_conf_wq_channel(device_t iodev, struct dpaa2_io_notif_ctx *ctx) +{ + struct dpaa2_io_softc *sc = device_get_softc(iodev); + + /* Enable generation of the CDAN notifications. */ + if (ctx->cdan_en) + return (dpaa2_swp_conf_wq_channel(sc->swp, ctx->fq_chan_id, + DPAA2_WQCHAN_WE_EN | DPAA2_WQCHAN_WE_CTX, ctx->cdan_en, + ctx->qman_ctx)); + + return (0); +} + +/** + * @brief Query current configuration/state of the buffer pool. + */ +static int +dpaa2_io_query_bp(device_t iodev, uint16_t bpid, struct dpaa2_bp_conf *conf) +{ + struct dpaa2_io_softc *sc = device_get_softc(iodev); + + return (dpaa2_swp_query_bp(sc->swp, bpid, conf)); +} + +/** + * @brief Release one or more buffer pointers to the QBMan buffer pool. + */ +static int +dpaa2_io_release_bufs(device_t iodev, uint16_t bpid, bus_addr_t *buf, + uint32_t buf_num) +{ + struct dpaa2_io_softc *sc = device_get_softc(iodev); + + return (dpaa2_swp_release_bufs(sc->swp, bpid, buf, buf_num)); +} + +/** + * @brief Configure DPNI object to generate interrupts. + */ +static int +dpaa2_io_setup_irqs(device_t dev) +{ + struct dpaa2_io_softc *sc = device_get_softc(dev); + int error; + + /* + * Setup interrupts generated by the software portal. + */ + dpaa2_swp_set_intr_trigger(sc->swp, DPAA2_SWP_INTR_DQRI); + dpaa2_swp_clear_intr_status(sc->swp, 0xFFFFFFFFu); + + /* Configure IRQs. */ + error = dpaa2_io_setup_msi(sc); + if (error) { + device_printf(dev, "%s: failed to allocate MSI: error=%d\n", + __func__, error); + return (error); + } + if ((sc->irq_resource = bus_alloc_resource_any(dev, SYS_RES_IRQ, + &sc->irq_rid[0], RF_ACTIVE | RF_SHAREABLE)) == NULL) { + device_printf(dev, "%s: failed to allocate IRQ resource\n", + __func__); + return (ENXIO); + } + if (bus_setup_intr(dev, sc->irq_resource, INTR_TYPE_NET | INTR_MPSAFE | + INTR_ENTROPY, NULL, dpaa2_io_intr, sc, &sc->intr)) { + device_printf(dev, "%s: failed to setup IRQ resource\n", + __func__); + return (ENXIO); + } + + /* Wrap DPIO ID around number of CPUs. */ + bus_bind_intr(dev, sc->irq_resource, sc->attr.id % mp_ncpus); + + /* + * Setup and enable Static Dequeue Command to receive CDANs from + * channel 0. + */ + if (sc->swp_desc.has_notif) + dpaa2_swp_set_push_dequeue(sc->swp, 0, true); + + return (0); +} + +static int +dpaa2_io_release_irqs(device_t dev) +{ + struct dpaa2_io_softc *sc = device_get_softc(dev); + + /* Disable receiving CDANs from channel 0. */ + if (sc->swp_desc.has_notif) + dpaa2_swp_set_push_dequeue(sc->swp, 0, false); + + /* Release IRQ resources. */ + if (sc->intr != NULL) + bus_teardown_intr(dev, sc->irq_resource, &sc->intr); + if (sc->irq_resource != NULL) + bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid[0], + sc->irq_resource); + + (void)dpaa2_io_release_msi(device_get_softc(dev)); + + /* Configure software portal to stop generating interrupts. */ + dpaa2_swp_set_intr_trigger(sc->swp, 0); + dpaa2_swp_clear_intr_status(sc->swp, 0xFFFFFFFFu); + + return (0); +} + +/** + * @brief Allocate MSI interrupts for this DPAA2 I/O object. + */ +static int +dpaa2_io_setup_msi(struct dpaa2_io_softc *sc) +{ + int val; + + val = pci_msi_count(sc->dev); + if (val < DPAA2_IO_MSI_COUNT) + device_printf(sc->dev, "MSI: actual=%d, expected=%d\n", val, + DPAA2_IO_MSI_COUNT); + val = MIN(val, DPAA2_IO_MSI_COUNT); + + if (pci_alloc_msi(sc->dev, &val) != 0) + return (EINVAL); + + for (int i = 0; i < val; i++) + sc->irq_rid[i] = i + 1; + + return (0); +} + +static int +dpaa2_io_release_msi(struct dpaa2_io_softc *sc) +{ + int error; + + error = pci_release_msi(sc->dev); + if (error) { + device_printf(sc->dev, "%s: failed to release MSI: error=%d/n", + __func__, error); + return (error); + } + + return (0); +} + +/** + * @brief DPAA2 I/O interrupt handler. + */ +static void +dpaa2_io_intr(void *arg) +{ + struct dpaa2_io_softc *sc = (struct dpaa2_io_softc *) arg; + struct dpaa2_io_notif_ctx *ctx[DPIO_POLL_MAX]; + struct dpaa2_dq dq; + uint32_t idx, status; + uint16_t flags; + int rc, cdan_n = 0; + + status = dpaa2_swp_read_intr_status(sc->swp); + if (status == 0) { + return; + } + + DPAA2_SWP_LOCK(sc->swp, &flags); + if (flags & DPAA2_SWP_DESTROYED) { + /* Terminate operation if portal is destroyed. */ + DPAA2_SWP_UNLOCK(sc->swp); + return; + } + + for (int i = 0; i < DPIO_POLL_MAX; i++) { + rc = dpaa2_swp_dqrr_next_locked(sc->swp, &dq, &idx); + if (rc) { + break; + } + + if ((dq.common.verb & DPAA2_DQRR_RESULT_MASK) == + DPAA2_DQRR_RESULT_CDAN) { + ctx[cdan_n++] = (struct dpaa2_io_notif_ctx *) dq.scn.ctx; + } else { + /* TODO: Report unknown DQRR entry. */ + } + dpaa2_swp_write_reg(sc->swp, DPAA2_SWP_CINH_DCAP, idx); + } + DPAA2_SWP_UNLOCK(sc->swp); + + for (int i = 0; i < cdan_n; i++) { + ctx[i]->poll(ctx[i]->channel); + } + + /* Enable software portal interrupts back */ + dpaa2_swp_clear_intr_status(sc->swp, status); + dpaa2_swp_write_reg(sc->swp, DPAA2_SWP_CINH_IIR, 0); +} + +static device_method_t dpaa2_io_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, dpaa2_io_probe), + DEVMETHOD(device_attach, dpaa2_io_attach), + DEVMETHOD(device_detach, dpaa2_io_detach), + + /* QBMan software portal interface */ + DEVMETHOD(dpaa2_swp_enq_multiple_fq, dpaa2_io_enq_multiple_fq), + DEVMETHOD(dpaa2_swp_conf_wq_channel, dpaa2_io_conf_wq_channel), + DEVMETHOD(dpaa2_swp_query_bp, dpaa2_io_query_bp), + DEVMETHOD(dpaa2_swp_release_bufs, dpaa2_io_release_bufs), + + DEVMETHOD_END +}; + +static driver_t dpaa2_io_driver = { + "dpaa2_io", + dpaa2_io_methods, + sizeof(struct dpaa2_io_softc), +}; + +DRIVER_MODULE(dpaa2_io, dpaa2_rc, dpaa2_io_driver, 0, 0); diff --git a/sys/dev/dpaa2/dpaa2_io.h b/sys/dev/dpaa2/dpaa2_io.h new file mode 100644 index 000000000000..d02dab8144df --- /dev/null +++ b/sys/dev/dpaa2/dpaa2_io.h @@ -0,0 +1,110 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright © 2021-2022 Dmitry Salychev + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _DPAA2_IO_H +#define _DPAA2_IO_H + +#include +#include +#include + +#include "dpaa2_types.h" +#include "dpaa2_mcp.h" + +/* Maximum resources per DPIO: 3 SYS_MEM + 1 DPMCP. */ +#define DPAA2_IO_MAX_RESOURCES 4 +/* Maximum number of MSIs supported by the DPIO objects. */ +#define DPAA2_IO_MSI_COUNT 1 + +enum dpaa2_io_chan_mode { + DPAA2_IO_NO_CHANNEL, + DPAA2_IO_LOCAL_CHANNEL +}; + +/** + * @brief Attributes of the DPIO object. + * + * swp_ce_paddr: Physical address of the cache-enabled area. + * swp_ci_paddr: Physical address of the cache-inhibited area. + * swp_version: Hardware IP version of the software portal. + * swp_clk: QBMAN clock frequency value in Hz. + * id: DPIO object ID. + * swp_id: Software portal ID. + * priors_num: Number of priorities for the notification channel (1-8); + * relevant only if channel mode is "local channel". + * chan_mode: Notification channel mode. + */ +struct dpaa2_io_attr { + uint64_t swp_ce_paddr; + uint64_t swp_ci_paddr; + uint32_t swp_version; + uint32_t swp_clk; + uint32_t id; + uint16_t swp_id; + uint8_t priors_num; + enum dpaa2_io_chan_mode chan_mode; +}; + +/** + * @brief Context used by DPIO to configure data availability notifications + * (CDAN) on a particular WQ channel. + */ +struct dpaa2_io_notif_ctx { + void (*poll)(void *); + + device_t io_dev; + void *channel; + uint64_t qman_ctx; + uint16_t fq_chan_id; + bool cdan_en; +}; + +/** + * @brief Software context for the DPAA2 I/O driver. + */ +struct dpaa2_io_softc { + device_t dev; + struct dpaa2_swp_desc swp_desc; + struct dpaa2_swp *swp; + struct dpaa2_io_attr attr; + + /* Help to send commands to MC. */ + struct dpaa2_cmd *cmd; + uint16_t rc_token; + uint16_t io_token; + + struct resource *res[DPAA2_IO_MAX_RESOURCES]; + struct resource_map map[DPAA2_IO_MAX_RESOURCES]; + + int irq_rid[DPAA2_IO_MSI_COUNT]; + struct resource *irq_resource; + void *intr; /* interrupt handle */ +}; + +extern struct resource_spec dpaa2_io_spec[]; + +#endif /* _DPAA2_IO_H */ diff --git a/sys/dev/dpaa2/dpaa2_mac.c b/sys/dev/dpaa2/dpaa2_mac.c new file mode 100644 index 000000000000..d6e381c0dd15 --- /dev/null +++ b/sys/dev/dpaa2/dpaa2_mac.c @@ -0,0 +1,376 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright © 2021-2022 Dmitry Salychev + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +/* + * The DPAA2 MAC driver. + * + * For every DPAA2 MAC, there is an MC object named DPMAC, for MDIO and link + * state updates. The DPMAC virtualizes the MDIO interface, so each PHY driver + * may see a private interface (removing the need for synchronization in GPP on + * the multiplexed MDIO hardware). + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#include "pcib_if.h" +#include "pci_if.h" + +#include "dpaa2_mc.h" +#include "dpaa2_ni.h" +#include "dpaa2_mcp.h" +#include "dpaa2_swp.h" +#include "dpaa2_swp_if.h" +#include "dpaa2_cmd_if.h" + +/* Index of the only DPMAC IRQ. */ +#define DPMAC_IRQ_INDEX 0 + +/* DPMAC IRQ statuses. */ +#define DPMAC_IRQ_LINK_CFG_REQ 0x00000001 /* change in requested link config. */ +#define DPMAC_IRQ_LINK_CHANGED 0x00000002 /* link state changed */ +#define DPMAC_IRQ_LINK_UP_REQ 0x00000004 /* link up request */ +#define DPMAC_IRQ_LINK_DOWN_REQ 0x00000008 /* link down request */ +#define DPMAC_IRQ_EP_CHANGED 0x00000010 /* DPAA2 endpoint dis/connected */ + +/* DPAA2 MAC resource specification. */ +struct resource_spec dpaa2_mac_spec[] = { + /* + * DPMCP resources. + * + * NOTE: MC command portals (MCPs) are used to send commands to, and + * receive responses from, the MC firmware. One portal per DPMAC. + */ +#define MCP_RES_NUM (1u) +#define MCP_RID_OFF (0u) +#define MCP_RID(rid) ((rid) + MCP_RID_OFF) + /* --- */ + { DPAA2_DEV_MCP, MCP_RID(0), RF_ACTIVE | RF_SHAREABLE | RF_OPTIONAL }, + /* --- */ + RESOURCE_SPEC_END +}; + +/* Interrupt configuration routines. */ +static int dpaa2_mac_setup_irq(device_t); +static int dpaa2_mac_setup_msi(struct dpaa2_mac_softc *); + +/* Subroutines to get text representation. */ +static const char *dpaa2_mac_ethif_to_str(enum dpaa2_mac_eth_if); +static const char *dpaa2_mac_link_type_to_str(enum dpaa2_mac_link_type); + +/* Interrupt handlers */ +static void dpaa2_mac_intr(void *arg); + +static int +dpaa2_mac_probe(device_t dev) +{ + /* DPIO device will be added by a parent resource container itself. */ + device_set_desc(dev, "DPAA2 MAC"); + return (BUS_PROBE_DEFAULT); +} + +static int +dpaa2_mac_attach(device_t dev) +{ + device_t pdev = device_get_parent(dev); + device_t child = dev; + device_t mcp_dev; + struct dpaa2_mac_softc *sc = device_get_softc(dev); + struct dpaa2_devinfo *rcinfo = device_get_ivars(pdev); + struct dpaa2_devinfo *dinfo = device_get_ivars(dev); + struct dpaa2_devinfo *mcp_dinfo; + int error; + + sc->dev = dev; + + memset(sc->addr, 0, ETHER_ADDR_LEN); + + error = bus_alloc_resources(sc->dev, dpaa2_mac_spec, sc->res); + if (error) { + device_printf(dev, "%s: failed to allocate resources: " + "error=%d\n", __func__, error); + return (ENXIO); + } + + /* Obtain MC portal. */ + mcp_dev = (device_t) rman_get_start(sc->res[MCP_RID(0)]); + mcp_dinfo = device_get_ivars(mcp_dev); + dinfo->portal = mcp_dinfo->portal; + + /* Allocate a command to send to MC hardware. */ + error = dpaa2_mcp_init_command(&sc->cmd, DPAA2_CMD_DEF); + if (error) { + device_printf(dev, "Failed to allocate dpaa2_cmd: error=%d\n", + error); + goto err_exit; + } + + /* Open resource container and DPMAC object. */ + error = DPAA2_CMD_RC_OPEN(dev, child, sc->cmd, rcinfo->id, + &sc->rc_token); + if (error) { + device_printf(dev, "Failed to open DPRC: error=%d\n", error); + goto err_free_cmd; + } + error = DPAA2_CMD_MAC_OPEN(dev, child, sc->cmd, dinfo->id, + &sc->mac_token); + if (error) { + device_printf(dev, "Failed to open DPMAC: id=%d, error=%d\n", + dinfo->id, error); + goto err_close_rc; + } + + error = DPAA2_CMD_MAC_GET_ATTRIBUTES(dev, child, sc->cmd, &sc->attr); + if (error) { + device_printf(dev, "Failed to get DPMAC attributes: id=%d, " + "error=%d\n", dinfo->id, error); + goto err_close_mac; + } + error = DPAA2_CMD_MAC_GET_ADDR(dev, child, sc->cmd, sc->addr); + if (error) + device_printf(dev, "Failed to get physical address: error=%d\n", + error); + /* + * TODO: Enable debug output via sysctl. + */ + if (bootverbose) { + device_printf(dev, "ether %6D\n", sc->addr, ":"); + device_printf(dev, "max_rate=%d, eth_if=%s, link_type=%s\n", + sc->attr.max_rate, + dpaa2_mac_ethif_to_str(sc->attr.eth_if), + dpaa2_mac_link_type_to_str(sc->attr.link_type)); + } + + error = dpaa2_mac_setup_irq(dev); + if (error) { + device_printf(dev, "Failed to setup IRQs: error=%d\n", error); + goto err_close_mac; + } + + return (0); + +err_close_mac: + DPAA2_CMD_MAC_CLOSE(dev, child, dpaa2_mcp_tk(sc->cmd, sc->mac_token)); +err_close_rc: + DPAA2_CMD_RC_CLOSE(dev, child, dpaa2_mcp_tk(sc->cmd, sc->rc_token)); +err_free_cmd: + dpaa2_mcp_free_command(sc->cmd); +err_exit: + return (ENXIO); +} + +static int +dpaa2_mac_detach(device_t dev) +{ + device_t child = dev; + struct dpaa2_mac_softc *sc = device_get_softc(dev); + + DPAA2_CMD_MAC_CLOSE(dev, child, dpaa2_mcp_tk(sc->cmd, sc->mac_token)); + DPAA2_CMD_RC_CLOSE(dev, child, dpaa2_mcp_tk(sc->cmd, sc->rc_token)); + dpaa2_mcp_free_command(sc->cmd); + + sc->cmd = NULL; + sc->rc_token = 0; + sc->mac_token = 0; + + return (0); +} + +/** + * @brief Configure DPMAC object to generate interrupts. + */ +static int +dpaa2_mac_setup_irq(device_t dev) +{ + device_t child = dev; + struct dpaa2_mac_softc *sc = device_get_softc(dev); + struct dpaa2_cmd *cmd = sc->cmd; + uint16_t mac_token = sc->mac_token; + uint32_t irq_mask; + int error; + + /* Configure IRQs. */ + error = dpaa2_mac_setup_msi(sc); + if (error) { + device_printf(dev, "Failed to allocate MSI\n"); + return (error); + } + if ((sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, + &sc->irq_rid[0], RF_ACTIVE | RF_SHAREABLE)) == NULL) { + device_printf(dev, "Failed to allocate IRQ resource\n"); + return (ENXIO); + } + if (bus_setup_intr(dev, sc->irq_res, INTR_TYPE_NET | INTR_MPSAFE, + NULL, dpaa2_mac_intr, sc, &sc->intr)) { + device_printf(dev, "Failed to setup IRQ resource\n"); + return (ENXIO); + } + + /* Configure DPNI to generate interrupts. */ + irq_mask = + DPMAC_IRQ_LINK_CFG_REQ | + DPMAC_IRQ_LINK_CHANGED | + DPMAC_IRQ_LINK_UP_REQ | + DPMAC_IRQ_LINK_DOWN_REQ | + DPMAC_IRQ_EP_CHANGED; + error = DPAA2_CMD_MAC_SET_IRQ_MASK(dev, child, dpaa2_mcp_tk(cmd, + mac_token), DPMAC_IRQ_INDEX, irq_mask); + if (error) { + device_printf(dev, "Failed to set IRQ mask\n"); + return (error); + } + + /* Enable IRQ. */ + error = DPAA2_CMD_MAC_SET_IRQ_ENABLE(dev, child, cmd, DPMAC_IRQ_INDEX, + true); + if (error) { + device_printf(dev, "Failed to enable IRQ\n"); + return (error); + } + + return (0); +} + +/** + * @brief Allocate MSI interrupts for DPMAC. + */ +static int +dpaa2_mac_setup_msi(struct dpaa2_mac_softc *sc) +{ + int val; + + val = pci_msi_count(sc->dev); + if (val < DPAA2_MAC_MSI_COUNT) + device_printf(sc->dev, "MSI: actual=%d, expected=%d\n", val, + DPAA2_MAC_MSI_COUNT); + val = MIN(val, DPAA2_MAC_MSI_COUNT); + + if (pci_alloc_msi(sc->dev, &val) != 0) + return (EINVAL); + + for (int i = 0; i < val; i++) + sc->irq_rid[i] = i + 1; + + return (0); +} + +static void +dpaa2_mac_intr(void *arg) +{ + struct dpaa2_mac_softc *sc = (struct dpaa2_mac_softc *) arg; + device_t child = sc->dev; + uint32_t status = ~0u; /* clear all IRQ status bits */ + int error; + + error = DPAA2_CMD_MAC_GET_IRQ_STATUS(sc->dev, child, + dpaa2_mcp_tk(sc->cmd, sc->mac_token), DPMAC_IRQ_INDEX, &status); + if (error) + device_printf(sc->dev, "%s: failed to obtain IRQ status: " + "error=%d\n", __func__, error); +} + +static const char * +dpaa2_mac_ethif_to_str(enum dpaa2_mac_eth_if eth_if) +{ + switch (eth_if) { + case DPAA2_MAC_ETH_IF_MII: + return ("MII"); + case DPAA2_MAC_ETH_IF_RMII: + return ("RMII"); + case DPAA2_MAC_ETH_IF_SMII: + return ("SMII"); + case DPAA2_MAC_ETH_IF_GMII: + return ("GMII"); + case DPAA2_MAC_ETH_IF_RGMII: + return ("RGMII"); + case DPAA2_MAC_ETH_IF_SGMII: + return ("SGMII"); + case DPAA2_MAC_ETH_IF_QSGMII: + return ("QSGMII"); + case DPAA2_MAC_ETH_IF_XAUI: + return ("XAUI"); + case DPAA2_MAC_ETH_IF_XFI: + return ("XFI"); + case DPAA2_MAC_ETH_IF_CAUI: + return ("CAUI"); + case DPAA2_MAC_ETH_IF_1000BASEX: + return ("1000BASE-X"); + case DPAA2_MAC_ETH_IF_USXGMII: + return ("USXGMII"); + default: + return ("unknown"); + } +} + +static const char * +dpaa2_mac_link_type_to_str(enum dpaa2_mac_link_type link_type) +{ + switch (link_type) { + case DPAA2_MAC_LINK_TYPE_NONE: + return ("NONE"); + case DPAA2_MAC_LINK_TYPE_FIXED: + return ("FIXED"); + case DPAA2_MAC_LINK_TYPE_PHY: + return ("PHY"); + case DPAA2_MAC_LINK_TYPE_BACKPLANE: + return ("BACKPLANE"); + default: + return ("unknown"); + } +} + +static device_method_t dpaa2_mac_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, dpaa2_mac_probe), + DEVMETHOD(device_attach, dpaa2_mac_attach), + DEVMETHOD(device_detach, dpaa2_mac_detach), + + DEVMETHOD_END +}; + +static driver_t dpaa2_mac_driver = { + "dpaa2_mac", + dpaa2_mac_methods, + sizeof(struct dpaa2_mac_softc), +}; + +DRIVER_MODULE(dpaa2_mac, dpaa2_rc, dpaa2_mac_driver, 0, 0); diff --git a/sys/dev/dpaa2/dpaa2_mac.h b/sys/dev/dpaa2/dpaa2_mac.h new file mode 100644 index 000000000000..cbdf2d824045 --- /dev/null +++ b/sys/dev/dpaa2/dpaa2_mac.h @@ -0,0 +1,124 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright © 2021-2022 Dmitry Salychev + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _DPAA2_MAC_H +#define _DPAA2_MAC_H + +#include +#include +#include + +#include + +#include "dpaa2_types.h" +#include "dpaa2_mcp.h" + +#define DPAA2_MAC_MAX_RESOURCES 1 /* Maximum resources per DPMAC: 1 DPMCP. */ +#define DPAA2_MAC_MSI_COUNT 1 /* MSIs per DPMAC */ + +/* DPMAC link configuration options. */ +#define DPAA2_MAC_LINK_OPT_AUTONEG ((uint64_t) 0x01u) +#define DPAA2_MAC_LINK_OPT_HALF_DUPLEX ((uint64_t) 0x02u) +#define DPAA2_MAC_LINK_OPT_PAUSE ((uint64_t) 0x04u) +#define DPAA2_MAC_LINK_OPT_ASYM_PAUSE ((uint64_t) 0x08u) + +enum dpaa2_mac_eth_if { + DPAA2_MAC_ETH_IF_MII, + DPAA2_MAC_ETH_IF_RMII, + DPAA2_MAC_ETH_IF_SMII, + DPAA2_MAC_ETH_IF_GMII, + DPAA2_MAC_ETH_IF_RGMII, + DPAA2_MAC_ETH_IF_SGMII, + DPAA2_MAC_ETH_IF_QSGMII, + DPAA2_MAC_ETH_IF_XAUI, + DPAA2_MAC_ETH_IF_XFI, + DPAA2_MAC_ETH_IF_CAUI, + DPAA2_MAC_ETH_IF_1000BASEX, + DPAA2_MAC_ETH_IF_USXGMII +}; + +enum dpaa2_mac_link_type { + DPAA2_MAC_LINK_TYPE_NONE, + DPAA2_MAC_LINK_TYPE_FIXED, + DPAA2_MAC_LINK_TYPE_PHY, + DPAA2_MAC_LINK_TYPE_BACKPLANE +}; + +/** + * @brief Attributes of the DPMAC object. + * + * id: DPMAC object ID. + * max_rate: Maximum supported rate (in Mbps). + * eth_if: Type of the Ethernet interface. + * link_type: Type of the link. + */ +struct dpaa2_mac_attr { + uint32_t id; + uint32_t max_rate; + enum dpaa2_mac_eth_if eth_if; + enum dpaa2_mac_link_type link_type; +}; + +/** + * @brief Link state of the DPMAC object. + */ +struct dpaa2_mac_link_state { + uint64_t options; + uint64_t supported; + uint64_t advert; + uint32_t rate; + bool up; + bool state_valid; +}; + +/** + * @brief Software context for the DPAA2 MAC driver. + * + * dev: Device associated with this software context. + * addr: Physical address assigned to the DPMAC object. + * attr: Attributes of the DPMAC object. + */ +struct dpaa2_mac_softc { + device_t dev; + uint8_t addr[ETHER_ADDR_LEN]; + struct resource *res[DPAA2_MAC_MAX_RESOURCES]; + struct dpaa2_mac_attr attr; + + /* Help to send commands to MC. */ + struct dpaa2_cmd *cmd; + uint16_t rc_token; + uint16_t mac_token; + + /* Interrupts. */ + int irq_rid[DPAA2_MAC_MSI_COUNT]; + struct resource *irq_res; + void *intr; /* interrupt handle */ +}; + +extern struct resource_spec dpaa2_mac_spec[]; + +#endif /* _DPAA2_MAC_H */ diff --git a/sys/dev/dpaa2/dpaa2_mc.c b/sys/dev/dpaa2/dpaa2_mc.c new file mode 100644 index 000000000000..e895d2585a3a --- /dev/null +++ b/sys/dev/dpaa2/dpaa2_mc.c @@ -0,0 +1,973 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright © 2021-2022 Dmitry Salychev + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +/* + * The DPAA2 Management Complex (MC) bus driver. + * + * MC is a hardware resource manager which can be found in several NXP + * SoCs (LX2160A, for example) and provides an access to the specialized + * hardware objects used in network-oriented packet processing applications. + */ + +#include "opt_acpi.h" +#include "opt_platform.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#ifdef DEV_ACPI +#include +#include +#endif + +#ifdef FDT +#include +#include +#include +#include +#endif + +#include "pcib_if.h" +#include "pci_if.h" + +#include "dpaa2_mc.h" + +/* Macros to read/write MC registers */ +#define mcreg_read_4(_sc, _r) bus_read_4(&(_sc)->map[1], (_r)) +#define mcreg_write_4(_sc, _r, _v) bus_write_4(&(_sc)->map[1], (_r), (_v)) + +#define COMPARE_TYPE(t, v) (strncmp((v), (t), strlen((v))) == 0) + +#define IORT_DEVICE_NAME "MCE" + +/* MC Registers */ +#define MC_REG_GCR1 0x0000u +#define MC_REG_GCR2 0x0004u /* TODO: Does it exist? */ +#define MC_REG_GSR 0x0008u +#define MC_REG_FAPR 0x0028u + +/* General Control Register 1 (GCR1) */ +#define GCR1_P1_STOP 0x80000000u +#define GCR1_P2_STOP 0x40000000u + +/* General Status Register (GSR) */ +#define GSR_HW_ERR(v) (((v) & 0x80000000u) >> 31) +#define GSR_CAT_ERR(v) (((v) & 0x40000000u) >> 30) +#define GSR_DPL_OFFSET(v) (((v) & 0x3FFFFF00u) >> 8) +#define GSR_MCS(v) (((v) & 0xFFu) >> 0) + +/* Timeouts to wait for the MC status. */ +#define MC_STAT_TIMEOUT 1000u /* us */ +#define MC_STAT_ATTEMPTS 100u + +/** + * @brief Structure to describe a DPAA2 device as a managed resource. + */ +struct dpaa2_mc_devinfo { + STAILQ_ENTRY(dpaa2_mc_devinfo) link; + device_t dpaa2_dev; + uint32_t flags; + uint32_t owners; +}; + +MALLOC_DEFINE(M_DPAA2_MC, "dpaa2_mc", "DPAA2 Management Complex"); + +static struct resource_spec dpaa2_mc_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE | RF_UNMAPPED }, + { SYS_RES_MEMORY, 1, RF_ACTIVE | RF_UNMAPPED | RF_OPTIONAL }, + RESOURCE_SPEC_END +}; + +static u_int dpaa2_mc_get_xref(device_t, device_t); +static u_int dpaa2_mc_map_id(device_t, device_t, uintptr_t *); +static struct rman *dpaa2_mc_rman(device_t, int); + +static int dpaa2_mc_alloc_msi_impl(device_t, device_t, int, int, int *); +static int dpaa2_mc_release_msi_impl(device_t, device_t, int, int *); +static int dpaa2_mc_map_msi_impl(device_t, device_t, int, uint64_t *, + uint32_t *); + +/* + * For device interface. + */ + +int +dpaa2_mc_attach(device_t dev) +{ + struct dpaa2_mc_softc *sc; + struct resource_map_request req; + uint32_t val; + int error; + + sc = device_get_softc(dev); + sc->dev = dev; + sc->msi_allocated = false; + sc->msi_owner = NULL; + + error = bus_alloc_resources(sc->dev, dpaa2_mc_spec, sc->res); + if (error) { + device_printf(dev, "%s: failed to allocate resources\n", + __func__); + return (ENXIO); + } + + if (sc->res[1]) { + resource_init_map_request(&req); + req.memattr = VM_MEMATTR_DEVICE; + error = bus_map_resource(sc->dev, SYS_RES_MEMORY, sc->res[1], + &req, &sc->map[1]); + if (error) { + device_printf(dev, "%s: failed to map control " + "registers\n", __func__); + dpaa2_mc_detach(dev); + return (ENXIO); + } + + if (bootverbose) + device_printf(dev, + "GCR1=0x%x, GCR2=0x%x, GSR=0x%x, FAPR=0x%x\n", + mcreg_read_4(sc, MC_REG_GCR1), + mcreg_read_4(sc, MC_REG_GCR2), + mcreg_read_4(sc, MC_REG_GSR), + mcreg_read_4(sc, MC_REG_FAPR)); + + /* Reset P1_STOP and P2_STOP bits to resume MC processor. */ + val = mcreg_read_4(sc, MC_REG_GCR1) & + ~(GCR1_P1_STOP | GCR1_P2_STOP); + mcreg_write_4(sc, MC_REG_GCR1, val); + + /* Poll MC status. */ + if (bootverbose) + device_printf(dev, "polling MC status...\n"); + for (int i = 0; i < MC_STAT_ATTEMPTS; i++) { + val = mcreg_read_4(sc, MC_REG_GSR); + if (GSR_MCS(val) != 0u) + break; + DELAY(MC_STAT_TIMEOUT); + } + + if (bootverbose) + device_printf(dev, + "GCR1=0x%x, GCR2=0x%x, GSR=0x%x, FAPR=0x%x\n", + mcreg_read_4(sc, MC_REG_GCR1), + mcreg_read_4(sc, MC_REG_GCR2), + mcreg_read_4(sc, MC_REG_GSR), + mcreg_read_4(sc, MC_REG_FAPR)); + } + + /* At least 64 bytes of the command portal should be available. */ + if (rman_get_size(sc->res[0]) < DPAA2_MCP_MEM_WIDTH) { + device_printf(dev, "%s: MC portal memory region too small: " + "%jd\n", __func__, rman_get_size(sc->res[0])); + dpaa2_mc_detach(dev); + return (ENXIO); + } + + /* Map MC portal memory resource. */ + resource_init_map_request(&req); + req.memattr = VM_MEMATTR_DEVICE; + error = bus_map_resource(sc->dev, SYS_RES_MEMORY, sc->res[0], + &req, &sc->map[0]); + if (error) { + device_printf(dev, "Failed to map MC portal memory\n"); + dpaa2_mc_detach(dev); + return (ENXIO); + } + + /* Initialize a resource manager for the DPAA2 I/O objects. */ + sc->dpio_rman.rm_type = RMAN_ARRAY; + sc->dpio_rman.rm_descr = "DPAA2 DPIO objects"; + error = rman_init(&sc->dpio_rman); + if (error) { + device_printf(dev, "Failed to initialize a resource manager for " + "the DPAA2 I/O objects: error=%d\n", error); + dpaa2_mc_detach(dev); + return (ENXIO); + } + + /* Initialize a resource manager for the DPAA2 buffer pools. */ + sc->dpbp_rman.rm_type = RMAN_ARRAY; + sc->dpbp_rman.rm_descr = "DPAA2 DPBP objects"; + error = rman_init(&sc->dpbp_rman); + if (error) { + device_printf(dev, "Failed to initialize a resource manager for " + "the DPAA2 buffer pools: error=%d\n", error); + dpaa2_mc_detach(dev); + return (ENXIO); + } + + /* Initialize a resource manager for the DPAA2 concentrators. */ + sc->dpcon_rman.rm_type = RMAN_ARRAY; + sc->dpcon_rman.rm_descr = "DPAA2 DPCON objects"; + error = rman_init(&sc->dpcon_rman); + if (error) { + device_printf(dev, "Failed to initialize a resource manager for " + "the DPAA2 concentrators: error=%d\n", error); + dpaa2_mc_detach(dev); + return (ENXIO); + } + + /* Initialize a resource manager for the DPAA2 MC portals. */ + sc->dpmcp_rman.rm_type = RMAN_ARRAY; + sc->dpmcp_rman.rm_descr = "DPAA2 DPMCP objects"; + error = rman_init(&sc->dpmcp_rman); + if (error) { + device_printf(dev, "Failed to initialize a resource manager for " + "the DPAA2 MC portals: error=%d\n", error); + dpaa2_mc_detach(dev); + return (ENXIO); + } + + /* Initialize a list of non-allocatable DPAA2 devices. */ + mtx_init(&sc->mdev_lock, "MC portal mdev lock", NULL, MTX_DEF); + STAILQ_INIT(&sc->mdev_list); + + mtx_init(&sc->msi_lock, "MC MSI lock", NULL, MTX_DEF); + + /* + * Add a root resource container as the only child of the bus. All of + * the direct descendant containers will be attached to the root one + * instead of the MC device. + */ + sc->rcdev = device_add_child(dev, "dpaa2_rc", 0); + if (sc->rcdev == NULL) { + dpaa2_mc_detach(dev); + return (ENXIO); + } + bus_generic_probe(dev); + bus_generic_attach(dev); + + return (0); +} + +int +dpaa2_mc_detach(device_t dev) +{ + struct dpaa2_mc_softc *sc; + struct dpaa2_devinfo *dinfo = NULL; + int error; + + bus_generic_detach(dev); + + sc = device_get_softc(dev); + if (sc->rcdev) + device_delete_child(dev, sc->rcdev); + bus_release_resources(dev, dpaa2_mc_spec, sc->res); + + dinfo = device_get_ivars(dev); + if (dinfo) + free(dinfo, M_DPAA2_MC); + + error = bus_generic_detach(dev); + if (error != 0) + return (error); + + return (device_delete_children(dev)); +} + +/* + * For bus interface. + */ + +struct resource * +dpaa2_mc_alloc_resource(device_t mcdev, device_t child, int type, int *rid, + rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) +{ + struct resource *res; + struct rman *rm; + int error; + + rm = dpaa2_mc_rman(mcdev, type); + if (!rm) + return (BUS_ALLOC_RESOURCE(device_get_parent(mcdev), child, + type, rid, start, end, count, flags)); + + /* + * Skip managing DPAA2-specific resource. It must be provided to MC by + * calling DPAA2_MC_MANAGE_DEV() beforehand. + */ + if (type <= DPAA2_DEV_MC) { + error = rman_manage_region(rm, start, end); + if (error) { + device_printf(mcdev, "rman_manage_region() failed: " + "start=%#jx, end=%#jx, error=%d\n", start, end, + error); + goto fail; + } + } + + res = rman_reserve_resource(rm, start, end, count, flags, child); + if (!res) { + device_printf(mcdev, "rman_reserve_resource() failed: " + "start=%#jx, end=%#jx, count=%#jx\n", start, end, count); + goto fail; + } + + rman_set_rid(res, *rid); + + if (flags & RF_ACTIVE) { + if (bus_activate_resource(child, type, *rid, res)) { + device_printf(mcdev, "bus_activate_resource() failed: " + "rid=%d, res=%#jx\n", *rid, (uintmax_t) res); + rman_release_resource(res); + goto fail; + } + } + + return (res); + fail: + device_printf(mcdev, "%s() failed: type=%d, rid=%d, start=%#jx, " + "end=%#jx, count=%#jx, flags=%x\n", __func__, type, *rid, start, end, + count, flags); + return (NULL); +} + +int +dpaa2_mc_adjust_resource(device_t mcdev, device_t child, int type, + struct resource *r, rman_res_t start, rman_res_t end) +{ + struct rman *rm; + + rm = dpaa2_mc_rman(mcdev, type); + if (rm) + return (rman_adjust_resource(r, start, end)); + return (bus_generic_adjust_resource(mcdev, child, type, r, start, end)); +} + +int +dpaa2_mc_release_resource(device_t mcdev, device_t child, int type, int rid, + struct resource *r) +{ + struct rman *rm; + + rm = dpaa2_mc_rman(mcdev, type); + if (rm) { + KASSERT(rman_is_region_manager(r, rm), ("rman mismatch")); + rman_release_resource(r); + } + + return (bus_generic_release_resource(mcdev, child, type, rid, r)); +} + +int +dpaa2_mc_activate_resource(device_t mcdev, device_t child, int type, int rid, + struct resource *r) +{ + int rc; + + if ((rc = rman_activate_resource(r)) != 0) + return (rc); + + return (BUS_ACTIVATE_RESOURCE(device_get_parent(mcdev), child, type, + rid, r)); +} + +int +dpaa2_mc_deactivate_resource(device_t mcdev, device_t child, int type, int rid, + struct resource *r) +{ + int rc; + + if ((rc = rman_deactivate_resource(r)) != 0) + return (rc); + + return (BUS_DEACTIVATE_RESOURCE(device_get_parent(mcdev), child, type, + rid, r)); +} + +/* + * For pseudo-pcib interface. + */ + +int +dpaa2_mc_alloc_msi(device_t mcdev, device_t child, int count, int maxcount, + int *irqs) +{ +#if defined(INTRNG) + return (dpaa2_mc_alloc_msi_impl(mcdev, child, count, maxcount, irqs)); +#else + return (ENXIO); +#endif +} + +int +dpaa2_mc_release_msi(device_t mcdev, device_t child, int count, int *irqs) +{ +#if defined(INTRNG) + return (dpaa2_mc_release_msi_impl(mcdev, child, count, irqs)); +#else + return (ENXIO); +#endif +} + +int +dpaa2_mc_map_msi(device_t mcdev, device_t child, int irq, uint64_t *addr, + uint32_t *data) +{ +#if defined(INTRNG) + return (dpaa2_mc_map_msi_impl(mcdev, child, irq, addr, data)); +#else + return (ENXIO); +#endif +} + +int +dpaa2_mc_get_id(device_t mcdev, device_t child, enum pci_id_type type, + uintptr_t *id) +{ + struct dpaa2_devinfo *dinfo; + + dinfo = device_get_ivars(child); + + if (strcmp(device_get_name(mcdev), "dpaa2_mc") != 0) + return (ENXIO); + + if (type == PCI_ID_MSI) + return (dpaa2_mc_map_id(mcdev, child, id)); + + *id = dinfo->icid; + return (0); +} + +/* + * For DPAA2 Management Complex bus driver interface. + */ + +int +dpaa2_mc_manage_dev(device_t mcdev, device_t dpaa2_dev, uint32_t flags) +{ + struct dpaa2_mc_softc *sc; + struct dpaa2_devinfo *dinfo; + struct dpaa2_mc_devinfo *di; + struct rman *rm; + int error; + + sc = device_get_softc(mcdev); + dinfo = device_get_ivars(dpaa2_dev); + + if (!sc || !dinfo || strcmp(device_get_name(mcdev), "dpaa2_mc") != 0) + return (EINVAL); + + di = malloc(sizeof(*di), M_DPAA2_MC, M_WAITOK | M_ZERO); + if (!di) + return (ENOMEM); + di->dpaa2_dev = dpaa2_dev; + di->flags = flags; + di->owners = 0; + + /* Append a new managed DPAA2 device to the queue. */ + mtx_assert(&sc->mdev_lock, MA_NOTOWNED); + mtx_lock(&sc->mdev_lock); + STAILQ_INSERT_TAIL(&sc->mdev_list, di, link); + mtx_unlock(&sc->mdev_lock); + + if (flags & DPAA2_MC_DEV_ALLOCATABLE) { + /* Select rman based on a type of the DPAA2 device. */ + rm = dpaa2_mc_rman(mcdev, dinfo->dtype); + if (!rm) + return (ENOENT); + /* Manage DPAA2 device as an allocatable resource. */ + error = rman_manage_region(rm, (rman_res_t) dpaa2_dev, + (rman_res_t) dpaa2_dev); + if (error) + return (error); + } + + return (0); +} + +int +dpaa2_mc_get_free_dev(device_t mcdev, device_t *dpaa2_dev, + enum dpaa2_dev_type devtype) +{ + struct rman *rm; + rman_res_t start, end; + int error; + + if (strcmp(device_get_name(mcdev), "dpaa2_mc") != 0) + return (EINVAL); + + /* Select resource manager based on a type of the DPAA2 device. */ + rm = dpaa2_mc_rman(mcdev, devtype); + if (!rm) + return (ENOENT); + /* Find first free DPAA2 device of the given type. */ + error = rman_first_free_region(rm, &start, &end); + if (error) + return (error); + + KASSERT(start == end, ("start != end, but should be the same pointer " + "to the DPAA2 device: start=%jx, end=%jx", start, end)); + + *dpaa2_dev = (device_t) start; + + return (0); +} + +int +dpaa2_mc_get_dev(device_t mcdev, device_t *dpaa2_dev, + enum dpaa2_dev_type devtype, uint32_t obj_id) +{ + struct dpaa2_mc_softc *sc; + struct dpaa2_devinfo *dinfo; + struct dpaa2_mc_devinfo *di; + int error = ENOENT; + + sc = device_get_softc(mcdev); + + if (!sc || strcmp(device_get_name(mcdev), "dpaa2_mc") != 0) + return (EINVAL); + + mtx_assert(&sc->mdev_lock, MA_NOTOWNED); + mtx_lock(&sc->mdev_lock); + + STAILQ_FOREACH(di, &sc->mdev_list, link) { + dinfo = device_get_ivars(di->dpaa2_dev); + if (dinfo->dtype == devtype && dinfo->id == obj_id) { + *dpaa2_dev = di->dpaa2_dev; + error = 0; + break; + } + } + + mtx_unlock(&sc->mdev_lock); + + return (error); +} + +int +dpaa2_mc_get_shared_dev(device_t mcdev, device_t *dpaa2_dev, + enum dpaa2_dev_type devtype) +{ + struct dpaa2_mc_softc *sc; + struct dpaa2_devinfo *dinfo; + struct dpaa2_mc_devinfo *di; + device_t dev = NULL; + uint32_t owners = UINT32_MAX; + int error = ENOENT; + + sc = device_get_softc(mcdev); + + if (!sc || strcmp(device_get_name(mcdev), "dpaa2_mc") != 0) + return (EINVAL); + + mtx_assert(&sc->mdev_lock, MA_NOTOWNED); + mtx_lock(&sc->mdev_lock); + + STAILQ_FOREACH(di, &sc->mdev_list, link) { + dinfo = device_get_ivars(di->dpaa2_dev); + + if ((dinfo->dtype == devtype) && + (di->flags & DPAA2_MC_DEV_SHAREABLE) && + (di->owners < owners)) { + dev = di->dpaa2_dev; + owners = di->owners; + } + } + if (dev) { + *dpaa2_dev = dev; + error = 0; + } + + mtx_unlock(&sc->mdev_lock); + + return (error); +} + +int +dpaa2_mc_reserve_dev(device_t mcdev, device_t dpaa2_dev, + enum dpaa2_dev_type devtype) +{ + struct dpaa2_mc_softc *sc; + struct dpaa2_mc_devinfo *di; + int error = ENOENT; + + sc = device_get_softc(mcdev); + + if (!sc || strcmp(device_get_name(mcdev), "dpaa2_mc") != 0) + return (EINVAL); + + mtx_assert(&sc->mdev_lock, MA_NOTOWNED); + mtx_lock(&sc->mdev_lock); + + STAILQ_FOREACH(di, &sc->mdev_list, link) { + if (di->dpaa2_dev == dpaa2_dev && + (di->flags & DPAA2_MC_DEV_SHAREABLE)) { + di->owners++; + error = 0; + break; + } + } + + mtx_unlock(&sc->mdev_lock); + + return (error); +} + +int +dpaa2_mc_release_dev(device_t mcdev, device_t dpaa2_dev, + enum dpaa2_dev_type devtype) +{ + struct dpaa2_mc_softc *sc; + struct dpaa2_mc_devinfo *di; + int error = ENOENT; + + sc = device_get_softc(mcdev); + + if (!sc || strcmp(device_get_name(mcdev), "dpaa2_mc") != 0) + return (EINVAL); + + mtx_assert(&sc->mdev_lock, MA_NOTOWNED); + mtx_lock(&sc->mdev_lock); + + STAILQ_FOREACH(di, &sc->mdev_list, link) { + if (di->dpaa2_dev == dpaa2_dev && + (di->flags & DPAA2_MC_DEV_SHAREABLE)) { + di->owners -= di->owners > 0 ? 1 : 0; + error = 0; + break; + } + } + + mtx_unlock(&sc->mdev_lock); + + return (error); +} + +/** + * @brief Convert DPAA2 device type to string. + */ +const char * +dpaa2_ttos(enum dpaa2_dev_type type) +{ + switch (type) { + case DPAA2_DEV_MC: + return ("mc"); /* NOTE: to print as information only. */ + case DPAA2_DEV_RC: + return ("dprc"); + case DPAA2_DEV_IO: + return ("dpio"); + case DPAA2_DEV_NI: + return ("dpni"); + case DPAA2_DEV_MCP: + return ("dpmcp"); + case DPAA2_DEV_BP: + return ("dpbp"); + case DPAA2_DEV_CON: + return ("dpcon"); + case DPAA2_DEV_MAC: + return ("dpmac"); + case DPAA2_DEV_MUX: + return ("dpdmux"); + case DPAA2_DEV_SW: + return ("dpsw"); + default: + break; + } + return ("notype"); +} + +/** + * @brief Convert string to DPAA2 device type. + */ +enum dpaa2_dev_type +dpaa2_stot(const char *str) +{ + if (COMPARE_TYPE(str, "dprc")) { + return (DPAA2_DEV_RC); + } else if (COMPARE_TYPE(str, "dpio")) { + return (DPAA2_DEV_IO); + } else if (COMPARE_TYPE(str, "dpni")) { + return (DPAA2_DEV_NI); + } else if (COMPARE_TYPE(str, "dpmcp")) { + return (DPAA2_DEV_MCP); + } else if (COMPARE_TYPE(str, "dpbp")) { + return (DPAA2_DEV_BP); + } else if (COMPARE_TYPE(str, "dpcon")) { + return (DPAA2_DEV_CON); + } else if (COMPARE_TYPE(str, "dpmac")) { + return (DPAA2_DEV_MAC); + } else if (COMPARE_TYPE(str, "dpdmux")) { + return (DPAA2_DEV_MUX); + } else if (COMPARE_TYPE(str, "dpsw")) { + return (DPAA2_DEV_SW); + } + + return (DPAA2_DEV_NOTYPE); +} + +/** + * @internal + */ +static u_int +dpaa2_mc_get_xref(device_t mcdev, device_t child) +{ + struct dpaa2_mc_softc *sc = device_get_softc(mcdev); + struct dpaa2_devinfo *dinfo = device_get_ivars(child); +#ifdef DEV_ACPI + u_int xref, devid; +#endif +#ifdef FDT + phandle_t msi_parent; +#endif + int error; + + if (sc && dinfo) { +#ifdef DEV_ACPI + if (sc->acpi_based) { + /* + * NOTE: The first named component from the IORT table + * with the given name (as a substring) will be used. + */ + error = acpi_iort_map_named_msi(IORT_DEVICE_NAME, + dinfo->icid, &xref, &devid); + if (error) + return (0); + return (xref); + } +#endif +#ifdef FDT + if (!sc->acpi_based) { + /* FDT-based driver. */ + error = ofw_bus_msimap(sc->ofw_node, dinfo->icid, + &msi_parent, NULL); + if (error) + return (0); + return ((u_int) msi_parent); + } +#endif + } + return (0); +} + +/** + * @internal + */ +static u_int +dpaa2_mc_map_id(device_t mcdev, device_t child, uintptr_t *id) +{ + struct dpaa2_devinfo *dinfo; +#ifdef DEV_ACPI + u_int xref, devid; + int error; +#endif + + dinfo = device_get_ivars(child); + if (dinfo) { + /* + * The first named components from IORT table with the given + * name (as a substring) will be used. + */ +#ifdef DEV_ACPI + error = acpi_iort_map_named_msi(IORT_DEVICE_NAME, dinfo->icid, + &xref, &devid); + if (error == 0) + *id = devid; + else +#endif + *id = dinfo->icid; /* RID not in IORT, likely FW bug */ + + return (0); + } + return (ENXIO); +} + +/** + * @internal + * @brief Obtain a resource manager based on the given type of the resource. + */ +static struct rman * +dpaa2_mc_rman(device_t mcdev, int type) +{ + struct dpaa2_mc_softc *sc; + + sc = device_get_softc(mcdev); + + switch (type) { + case DPAA2_DEV_IO: + return (&sc->dpio_rman); + case DPAA2_DEV_BP: + return (&sc->dpbp_rman); + case DPAA2_DEV_CON: + return (&sc->dpcon_rman); + case DPAA2_DEV_MCP: + return (&sc->dpmcp_rman); + default: + break; + } + + return (NULL); +} + +#if defined(INTRNG) && !defined(IOMMU) + +/** + * @internal + * @brief Allocates requested number of MSIs. + * + * NOTE: This function is a part of fallback solution when IOMMU isn't available. + * Total number of IRQs is limited to 32. + */ +static int +dpaa2_mc_alloc_msi_impl(device_t mcdev, device_t child, int count, int maxcount, + int *irqs) +{ + struct dpaa2_mc_softc *sc = device_get_softc(mcdev); + int msi_irqs[DPAA2_MC_MSI_COUNT]; + int error; + + /* Pre-allocate a bunch of MSIs for MC to be used by its children. */ + if (!sc->msi_allocated) { + error = intr_alloc_msi(mcdev, child, dpaa2_mc_get_xref(mcdev, + child), DPAA2_MC_MSI_COUNT, DPAA2_MC_MSI_COUNT, msi_irqs); + if (error) { + device_printf(mcdev, "failed to pre-allocate %d MSIs: " + "error=%d\n", DPAA2_MC_MSI_COUNT, error); + return (error); + } + + mtx_assert(&sc->msi_lock, MA_NOTOWNED); + mtx_lock(&sc->msi_lock); + for (int i = 0; i < DPAA2_MC_MSI_COUNT; i++) { + sc->msi[i].child = NULL; + sc->msi[i].irq = msi_irqs[i]; + } + sc->msi_owner = child; + sc->msi_allocated = true; + mtx_unlock(&sc->msi_lock); + } + + error = ENOENT; + + /* Find the first free MSIs from the pre-allocated pool. */ + mtx_assert(&sc->msi_lock, MA_NOTOWNED); + mtx_lock(&sc->msi_lock); + for (int i = 0; i < DPAA2_MC_MSI_COUNT; i++) { + if (sc->msi[i].child != NULL) + continue; + error = 0; + for (int j = 0; j < count; j++) { + if (i + j >= DPAA2_MC_MSI_COUNT) { + device_printf(mcdev, "requested %d MSIs exceed " + "limit of %d available\n", count, + DPAA2_MC_MSI_COUNT); + error = E2BIG; + break; + } + sc->msi[i + j].child = child; + irqs[j] = sc->msi[i + j].irq; + } + break; + } + mtx_unlock(&sc->msi_lock); + + return (error); +} + +/** + * @internal + * @brief Marks IRQs as free in the pre-allocated pool of MSIs. + * + * NOTE: This function is a part of fallback solution when IOMMU isn't available. + * Total number of IRQs is limited to 32. + * NOTE: MSIs are kept allocated in the kernel as a part of the pool. + */ +static int +dpaa2_mc_release_msi_impl(device_t mcdev, device_t child, int count, int *irqs) +{ + struct dpaa2_mc_softc *sc = device_get_softc(mcdev); + + mtx_assert(&sc->msi_lock, MA_NOTOWNED); + mtx_lock(&sc->msi_lock); + for (int i = 0; i < DPAA2_MC_MSI_COUNT; i++) { + if (sc->msi[i].child != child) + continue; + for (int j = 0; j < count; j++) { + if (sc->msi[i].irq == irqs[j]) { + sc->msi[i].child = NULL; + break; + } + } + } + mtx_unlock(&sc->msi_lock); + + return (0); +} + +/** + * @internal + * @brief Provides address to write to and data according to the given MSI from + * the pre-allocated pool. + * + * NOTE: This function is a part of fallback solution when IOMMU isn't available. + * Total number of IRQs is limited to 32. + */ +static int +dpaa2_mc_map_msi_impl(device_t mcdev, device_t child, int irq, uint64_t *addr, + uint32_t *data) +{ + struct dpaa2_mc_softc *sc = device_get_softc(mcdev); + int error = EINVAL; + + mtx_assert(&sc->msi_lock, MA_NOTOWNED); + mtx_lock(&sc->msi_lock); + for (int i = 0; i < DPAA2_MC_MSI_COUNT; i++) { + if (sc->msi[i].child == child && sc->msi[i].irq == irq) { + error = 0; + break; + } + } + mtx_unlock(&sc->msi_lock); + if (error) + return (error); + + return (intr_map_msi(mcdev, sc->msi_owner, dpaa2_mc_get_xref(mcdev, + sc->msi_owner), irq, addr, data)); +} + +#endif /* defined(INTRNG) && !defined(IOMMU) */ + +static device_method_t dpaa2_mc_methods[] = { + DEVMETHOD_END +}; + +DEFINE_CLASS_0(dpaa2_mc, dpaa2_mc_driver, dpaa2_mc_methods, + sizeof(struct dpaa2_mc_softc)); diff --git a/sys/dev/dpaa2/dpaa2_mc.h b/sys/dev/dpaa2/dpaa2_mc.h new file mode 100644 index 000000000000..9a21c9724b82 --- /dev/null +++ b/sys/dev/dpaa2/dpaa2_mc.h @@ -0,0 +1,218 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright © 2021-2022 Dmitry Salychev + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _DPAA2_MC_H +#define _DPAA2_MC_H + +#include +#include +#include + +#include + +#include + +#include "pci_if.h" + +#include "dpaa2_types.h" +#include "dpaa2_mcp.h" +#include "dpaa2_swp.h" +#include "dpaa2_ni.h" +#include "dpaa2_io.h" +#include "dpaa2_mac.h" +#include "dpaa2_con.h" +#include "dpaa2_bp.h" + +/* + * Maximum number of MSIs supported by the MC for its children without IOMMU. + * + * TODO: Should be much more with IOMMU translation. + */ +#define DPAA2_MC_MSI_COUNT 32 + +/* Flags for DPAA2 devices as resources. */ +#define DPAA2_MC_DEV_ALLOCATABLE 0x01u /* to be managed by DPAA2-specific rman */ +#define DPAA2_MC_DEV_ASSOCIATED 0x02u /* to obtain info about DPAA2 device */ +#define DPAA2_MC_DEV_SHAREABLE 0x04u /* to be shared among DPAA2 devices */ + +struct dpaa2_mc_devinfo; /* about managed DPAA2 devices */ + +/** + * @brief Software context for the DPAA2 Management Complex (MC) driver. + * + * dev: Device associated with this software context. + * rcdev: Child device associated with the root resource container. + * acpi_based: Attached using ACPI (true) or FDT (false). + * ofw_node: FDT node of the Management Complex (acpi_based == false). + * + * res: Unmapped MC command portal and control registers resources. + * map: Mapped MC command portal and control registers resources. + * + * dpio_rman: I/O objects resource manager. + * dpbp_rman: Buffer Pools resource manager. + * dpcon_rman: Concentrators resource manager. + * dpmcp_rman: MC portals resource manager. + */ +struct dpaa2_mc_softc { + device_t dev; + device_t rcdev; + bool acpi_based; + phandle_t ofw_node; + + struct resource *res[2]; + struct resource_map map[2]; + + /* For allocatable managed DPAA2 objects. */ + struct rman dpio_rman; + struct rman dpbp_rman; + struct rman dpcon_rman; + struct rman dpmcp_rman; + + /* For managed DPAA2 objects. */ + struct mtx mdev_lock; + STAILQ_HEAD(, dpaa2_mc_devinfo) mdev_list; + + /* NOTE: Workaround in case of no IOMMU available. */ +#ifndef IOMMU + device_t msi_owner; + bool msi_allocated; + struct mtx msi_lock; + struct { + device_t child; + int irq; + } msi[DPAA2_MC_MSI_COUNT]; +#endif +}; + +/** + * @brief Software context for the DPAA2 Resource Container (RC) driver. + * + * dev: Device associated with this software context. + * portal: Helper object to send commands to the MC portal. + * unit: Helps to distinguish between root (0) and child DRPCs. + * cont_id: Container ID. + */ +struct dpaa2_rc_softc { + device_t dev; + int unit; + uint32_t cont_id; +}; + +/** + * @brief Information about MSI messages supported by the DPAA2 object. + * + * msi_msgnum: Number of MSI messages supported by the DPAA2 object. + * msi_alloc: Number of MSI messages allocated for the DPAA2 object. + * msi_handlers: Number of MSI message handlers configured. + */ +struct dpaa2_msinfo { + uint8_t msi_msgnum; + uint8_t msi_alloc; + uint32_t msi_handlers; +}; + +/** + * @brief Information about DPAA2 device. + * + * pdev: Parent device. + * dev: Device this devinfo is associated with. + * + * id: ID of a logical DPAA2 object resource. + * portal_id: ID of the MC portal which belongs to the object's container. + * icid: Isolation context ID of the DPAA2 object. It is shared + * between a resource container and all of its children. + * + * dtype: Type of the DPAA2 object. + * resources: Resources available for this DPAA2 device. + * msi: Information about MSI messages supported by the DPAA2 object. + */ +struct dpaa2_devinfo { + device_t pdev; + device_t dev; + + uint32_t id; + uint32_t portal_id; + uint32_t icid; + + enum dpaa2_dev_type dtype; + struct resource_list resources; + struct dpaa2_msinfo msi; + + /* + * DPAA2 object might or might not have its own portal allocated to + * execute MC commands. If the portal has been allocated, it takes + * precedence over the portal owned by the resource container. + */ + struct dpaa2_mcp *portal; +}; + +DECLARE_CLASS(dpaa2_mc_driver); + +/* For device interface. */ + +int dpaa2_mc_attach(device_t dev); +int dpaa2_mc_detach(device_t dev); + +/* For bus interface. */ + +struct resource * dpaa2_mc_alloc_resource(device_t mcdev, device_t child, + int type, int *rid, rman_res_t start, rman_res_t end, rman_res_t count, + u_int flags); +int dpaa2_mc_adjust_resource(device_t mcdev, device_t child, int type, + struct resource *r, rman_res_t start, rman_res_t end); +int dpaa2_mc_release_resource(device_t mcdev, device_t child, int type, + int rid, struct resource *r); +int dpaa2_mc_activate_resource(device_t mcdev, device_t child, int type, + int rid, struct resource *r); +int dpaa2_mc_deactivate_resource(device_t mcdev, device_t child, int type, + int rid, struct resource *r); + +/* For pseudo-pcib interface. */ + +int dpaa2_mc_alloc_msi(device_t mcdev, device_t child, int count, int maxcount, + int *irqs); +int dpaa2_mc_release_msi(device_t mcdev, device_t child, int count, int *irqs); +int dpaa2_mc_map_msi(device_t mcdev, device_t child, int irq, uint64_t *addr, + uint32_t *data); +int dpaa2_mc_get_id(device_t mcdev, device_t child, enum pci_id_type type, + uintptr_t *id); + +/* For DPAA2 MC bus interface. */ + +int dpaa2_mc_manage_dev(device_t mcdev, device_t dpaa2_dev, uint32_t flags); +int dpaa2_mc_get_free_dev(device_t mcdev, device_t *dpaa2_dev, + enum dpaa2_dev_type devtype); +int dpaa2_mc_get_dev(device_t mcdev, device_t *dpaa2_dev, + enum dpaa2_dev_type devtype, uint32_t obj_id); +int dpaa2_mc_get_shared_dev(device_t mcdev, device_t *dpaa2_dev, + enum dpaa2_dev_type devtype); +int dpaa2_mc_reserve_dev(device_t mcdev, device_t dpaa2_dev, + enum dpaa2_dev_type devtype); +int dpaa2_mc_release_dev(device_t mcdev, device_t dpaa2_dev, + enum dpaa2_dev_type devtype); + +#endif /* _DPAA2_MC_H */ diff --git a/sys/dev/dpaa2/dpaa2_mc_acpi.c b/sys/dev/dpaa2/dpaa2_mc_acpi.c new file mode 100644 index 000000000000..fb0b467b5009 --- /dev/null +++ b/sys/dev/dpaa2/dpaa2_mc_acpi.c @@ -0,0 +1,393 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright © 2021-2022 Dmitry Salychev + * Copyright © 2021 Bjoern A. Zeeb + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +/* + * The DPAA2 Management Complex (MC) Bus Driver (ACPI-based). + * + * MC is a hardware resource manager which can be found in several NXP + * SoCs (LX2160A, for example) and provides an access to the specialized + * hardware objects used in network-oriented packet processing applications. + */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "acpi_bus_if.h" +#include "pcib_if.h" +#include "pci_if.h" + +#include "dpaa2_mcp.h" +#include "dpaa2_mc.h" +#include "dpaa2_mc_if.h" + +struct dpaa2_mac_dev_softc { + int uid; + uint64_t reg; + char managed[64]; + char phy_conn_type[64]; + char phy_mode[64]; + ACPI_HANDLE phy_channel; +}; + +static int +dpaa2_mac_dev_probe(device_t dev) +{ + uint64_t reg; + ssize_t s; + + s = device_get_property(dev, "reg", ®, sizeof(reg), + DEVICE_PROP_UINT64); + if (s == -1) + return (ENXIO); + + device_set_desc(dev, "DPAA2 MAC DEV"); + return (BUS_PROBE_DEFAULT); +} + +static int +dpaa2_mac_dev_attach(device_t dev) +{ + struct dpaa2_mac_dev_softc *sc; + ACPI_HANDLE h; + ssize_t s; + + sc = device_get_softc(dev); + h = acpi_get_handle(dev); + if (h == NULL) + return (ENXIO); + + s = acpi_GetInteger(h, "_UID", &sc->uid); + if (ACPI_FAILURE(s)) { + device_printf(dev, "Cannot find '_UID' property: %zd\n", s); + return (ENXIO); + } + + s = device_get_property(dev, "reg", &sc->reg, sizeof(sc->reg), + DEVICE_PROP_UINT64); + if (s == -1) { + device_printf(dev, "Cannot find 'reg' property: %zd\n", s); + return (ENXIO); + } + + s = device_get_property(dev, "managed", sc->managed, + sizeof(sc->managed), DEVICE_PROP_ANY); + s = device_get_property(dev, "phy-connection-type", sc->phy_conn_type, + sizeof(sc->phy_conn_type), DEVICE_PROP_ANY); + s = device_get_property(dev, "phy-mode", sc->phy_mode, + sizeof(sc->phy_mode), DEVICE_PROP_ANY); + s = device_get_property(dev, "phy-handle", &sc->phy_channel, + sizeof(sc->phy_channel), DEVICE_PROP_HANDLE); + + if (bootverbose) + device_printf(dev, "UID %#04x reg %#04jx managed '%s' " + "phy-connection-type '%s' phy-mode '%s' phy-handle '%s'\n", + sc->uid, sc->reg, sc->managed[0] != '\0' ? sc->managed : "", + sc->phy_conn_type[0] != '\0' ? sc->phy_conn_type : "", + sc->phy_mode[0] != '\0' ? sc->phy_mode : "", + sc->phy_channel != NULL ? acpi_name(sc->phy_channel) : ""); + + return (0); +} + +static bool +dpaa2_mac_dev_match_id(device_t dev, uint32_t id) +{ + struct dpaa2_mac_dev_softc *sc; + + if (dev == NULL) + return (false); + + sc = device_get_softc(dev); + if (sc->uid == id) + return (true); + + return (false); +} + +static device_t +dpaa2_mac_dev_get_phy_dev(device_t dev) +{ + struct dpaa2_mac_dev_softc *sc; + + if (dev == NULL) + return (NULL); + + sc = device_get_softc(dev); + if (sc->phy_channel == NULL) + return (NULL); + + return (acpi_get_device(sc->phy_channel)); +} + +static device_method_t dpaa2_mac_dev_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, dpaa2_mac_dev_probe), + DEVMETHOD(device_attach, dpaa2_mac_dev_attach), + DEVMETHOD(device_detach, bus_generic_detach), + + DEVMETHOD_END +}; + +DEFINE_CLASS_0(dpaa2_mac_dev, dpaa2_mac_dev_driver, dpaa2_mac_dev_methods, + sizeof(struct dpaa2_mac_dev_softc)); + +DRIVER_MODULE(dpaa2_mac_dev, dpaa2_mc, dpaa2_mac_dev_driver, 0, 0); + +MODULE_DEPEND(dpaa2_mac_dev, memac_mdio_acpi, 1, 1, 1); + +/* + * Device interface. + */ + +static int +dpaa2_mc_acpi_probe(device_t dev) +{ + static char *dpaa2_mc_ids[] = { "NXP0008", NULL }; + int rc; + + ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__); + + rc = ACPI_ID_PROBE(device_get_parent(dev), dev, dpaa2_mc_ids, NULL); + if (rc <= 0) + device_set_desc(dev, "DPAA2 Management Complex"); + + return (rc); +} + +/* Context for walking PRxx child devices. */ +struct dpaa2_mc_acpi_prxx_walk_ctx { + device_t dev; + int count; + int countok; +}; + +static ACPI_STATUS +dpaa2_mc_acpi_probe_child(ACPI_HANDLE h, device_t *dev, int level, void *arg) +{ + struct dpaa2_mc_acpi_prxx_walk_ctx *ctx; + struct acpi_device *ad; + device_t child; + uint32_t uid; + + ctx = (struct dpaa2_mc_acpi_prxx_walk_ctx *)arg; + ctx->count++; + +#if 0 + device_printf(ctx->dev, "%s: %s level %d count %d\n", __func__, + acpi_name(h), level, ctx->count); +#endif + + if (ACPI_FAILURE(acpi_GetInteger(h, "_UID", &uid))) + return (AE_OK); +#if 0 + if (bootverbose) + device_printf(ctx->dev, "%s: Found child Ports _UID %u\n", + __func__, uid); +#endif + + /* Technically M_ACPIDEV */ + if ((ad = malloc(sizeof(*ad), M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) + return (AE_OK); + + child = device_add_child(ctx->dev, "dpaa2_mac_dev", -1); + if (child == NULL) { + free(ad, M_DEVBUF); + return (AE_OK); + } + ad->ad_handle = h; + ad->ad_cls_class = 0xffffff; + resource_list_init(&ad->ad_rl); + device_set_ivars(child, ad); + *dev = child; + + ctx->countok++; + return (AE_OK); +} + +static int +dpaa2_mc_acpi_attach(device_t dev) +{ + struct dpaa2_mc_softc *sc; + + sc = device_get_softc(dev); + sc->acpi_based = true; + + struct dpaa2_mc_acpi_prxx_walk_ctx ctx; + ctx.dev = dev; + ctx.count = 0; + ctx.countok = 0; + ACPI_SCAN_CHILDREN(device_get_parent(dev), dev, 2, + dpaa2_mc_acpi_probe_child, &ctx); + +#if 0 + device_printf(dev, "Found %d child Ports in ASL, %d ok\n", + ctx.count, ctx.countok); +#endif + + return (dpaa2_mc_attach(dev)); +} + +/* + * ACPI compat layer. + */ + +static device_t +dpaa2_mc_acpi_find_dpaa2_mac_dev(device_t dev, uint32_t id) +{ + int devcount, error, i, len; + device_t *devlist, mdev; + const char *mdevname; + + error = device_get_children(dev, &devlist, &devcount); + if (error != 0) + return (NULL); + + for (i = 0; i < devcount; i++) { + mdev = devlist[i]; + mdevname = device_get_name(mdev); + if (mdevname != NULL) { + len = strlen(mdevname); + if (strncmp("dpaa2_mac_dev", mdevname, len) != 0) + continue; + } else { + continue; + } + if (!device_is_attached(mdev)) + continue; + + if (dpaa2_mac_dev_match_id(mdev, id)) + return (mdev); + } + + return (NULL); +} + +static int +dpaa2_mc_acpi_get_phy_dev(device_t dev, device_t *phy_dev, uint32_t id) +{ + device_t mdev, pdev; + + mdev = dpaa2_mc_acpi_find_dpaa2_mac_dev(dev, id); + if (mdev == NULL) { + device_printf(dev, "%s: error finding dpmac device with id=%u\n", + __func__, id); + return (ENXIO); + } + + pdev = dpaa2_mac_dev_get_phy_dev(mdev); + if (pdev == NULL) { + device_printf(dev, "%s: error getting MDIO device for dpamc %s " + "(id=%u)\n", __func__, device_get_nameunit(mdev), id); + return (ENXIO); + } + + if (phy_dev != NULL) + *phy_dev = pdev; + + return (0); +} + +static ssize_t +dpaa2_mc_acpi_get_property(device_t dev, device_t child, const char *propname, + void *propvalue, size_t size, device_property_type_t type) +{ + return (bus_generic_get_property(dev, child, propname, propvalue, size, + type)); +} + +static int +dpaa2_mc_acpi_read_ivar(device_t dev, device_t child, int index, + uintptr_t *result) +{ + /* + * This is special in that it passes "child" as second argument rather + * than "dev". acpi_get_handle() in dpaa2_mac_dev_attach() calls the + * read on parent(dev), dev and gets us here not to ACPI. Hence we + * need to keep child as-is and pass it to our parent which is ACPI. + * Only that gives the desired result. + */ + return (BUS_READ_IVAR(device_get_parent(dev), child, index, result)); +} + +static device_method_t dpaa2_mc_acpi_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, dpaa2_mc_acpi_probe), + DEVMETHOD(device_attach, dpaa2_mc_acpi_attach), + DEVMETHOD(device_detach, dpaa2_mc_detach), + + /* Bus interface */ + DEVMETHOD(bus_alloc_resource, dpaa2_mc_alloc_resource), + DEVMETHOD(bus_adjust_resource, dpaa2_mc_adjust_resource), + DEVMETHOD(bus_release_resource, dpaa2_mc_release_resource), + DEVMETHOD(bus_activate_resource, dpaa2_mc_activate_resource), + DEVMETHOD(bus_deactivate_resource, dpaa2_mc_deactivate_resource), + DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), + DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), + + /* Pseudo-PCIB interface */ + DEVMETHOD(pcib_alloc_msi, dpaa2_mc_alloc_msi), + DEVMETHOD(pcib_release_msi, dpaa2_mc_release_msi), + DEVMETHOD(pcib_map_msi, dpaa2_mc_map_msi), + DEVMETHOD(pcib_get_id, dpaa2_mc_get_id), + + /* DPAA2 MC bus interface */ + DEVMETHOD(dpaa2_mc_manage_dev, dpaa2_mc_manage_dev), + DEVMETHOD(dpaa2_mc_get_free_dev,dpaa2_mc_get_free_dev), + DEVMETHOD(dpaa2_mc_get_dev, dpaa2_mc_get_dev), + DEVMETHOD(dpaa2_mc_get_shared_dev, dpaa2_mc_get_shared_dev), + DEVMETHOD(dpaa2_mc_reserve_dev, dpaa2_mc_reserve_dev), + DEVMETHOD(dpaa2_mc_release_dev, dpaa2_mc_release_dev), + DEVMETHOD(dpaa2_mc_get_phy_dev, dpaa2_mc_acpi_get_phy_dev), + + /* ACPI compar layer. */ + DEVMETHOD(bus_read_ivar, dpaa2_mc_acpi_read_ivar), + DEVMETHOD(bus_get_property, dpaa2_mc_acpi_get_property), + + DEVMETHOD_END +}; + +DEFINE_CLASS_1(dpaa2_mc, dpaa2_mc_acpi_driver, dpaa2_mc_acpi_methods, + sizeof(struct dpaa2_mc_softc), dpaa2_mc_driver); + +/* Make sure miibus gets procesed first. */ +DRIVER_MODULE_ORDERED(dpaa2_mc, acpi, dpaa2_mc_acpi_driver, NULL, NULL, + SI_ORDER_ANY); +MODULE_DEPEND(dpaa2_mc, memac_mdio_acpi, 1, 1, 1); diff --git a/sys/dev/dpaa2/dpaa2_mc_fdt.c b/sys/dev/dpaa2/dpaa2_mc_fdt.c new file mode 100644 index 000000000000..ef3ff14c2dd6 --- /dev/null +++ b/sys/dev/dpaa2/dpaa2_mc_fdt.c @@ -0,0 +1,399 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright © 2021-2022 Dmitry Salychev + * Copyright © 2022 Bjoern A. Zeeb + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +/* + * The DPAA2 Management Complex (MC) Bus Driver (FDT-based). + * + * MC is a hardware resource manager which can be found in several NXP + * SoCs (LX2160A, for example) and provides an access to the specialized + * hardware objects used in network-oriented packet processing applications. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "pcib_if.h" +#include "pci_if.h" +#include "ofw_bus_if.h" + +#include "dpaa2_mcp.h" +#include "dpaa2_mc.h" +#include "dpaa2_mc_if.h" + +struct dpaa2_mac_fdt_softc { + uint32_t reg; + phandle_t sfp; + phandle_t pcs_handle; + phandle_t phy_handle; + char managed[64]; + char phy_conn_type[64]; +}; + +#if 0 + ethernet@1 { + + compatible = "fsl,qoriq-mc-dpmac"; + reg = <0x1>; + sfp = <0x14>; + pcs-handle = <0x15>; + phy-connection-type = "10gbase-r"; + managed = "in-band-status"; + }; + ethernet@3 { + + compatible = "fsl,qoriq-mc-dpmac"; + reg = <0x3>; + phy-handle = <0x18>; + phy-connection-type = "qsgmii"; + managed = "in-band-status"; + pcs-handle = <0x19>; + }; +#endif + +static int +dpaa2_mac_dev_probe(device_t dev) +{ + phandle_t node; + uint64_t reg; + ssize_t s; + + node = ofw_bus_get_node(dev); + if (!ofw_bus_node_is_compatible(node, "fsl,qoriq-mc-dpmac")) { + device_printf(dev, "'%s' not fsl,qoriq-mc-dpmac compatible\n", + ofw_bus_get_name(dev)); + return (ENXIO); + } + + s = device_get_property(dev, "reg", ®, sizeof(reg), + DEVICE_PROP_UINT32); + if (s == -1) { + device_printf(dev, "%s: '%s' has no 'reg' property, s %zd\n", + __func__, ofw_bus_get_name(dev), s); + return (ENXIO); + } + + device_set_desc(dev, "DPAA2 MAC DEV"); + return (BUS_PROBE_DEFAULT); +} + +static int +dpaa2_mac_fdt_attach(device_t dev) +{ + struct dpaa2_mac_fdt_softc *sc; + phandle_t node; + ssize_t s; + + sc = device_get_softc(dev); + node = ofw_bus_get_node(dev); + + s = device_get_property(dev, "reg", &sc->reg, sizeof(sc->reg), + DEVICE_PROP_UINT32); + if (s == -1) { + device_printf(dev, "Cannot find 'reg' property: %zd\n", s); + return (ENXIO); + } + + s = device_get_property(dev, "managed", sc->managed, + sizeof(sc->managed), DEVICE_PROP_ANY); + s = device_get_property(dev, "phy-connection-type", sc->phy_conn_type, + sizeof(sc->phy_conn_type), DEVICE_PROP_ANY); + s = device_get_property(dev, "pcs-handle", &sc->pcs_handle, + sizeof(sc->pcs_handle), DEVICE_PROP_HANDLE); + + /* 'sfp' and 'phy-handle' are optional but we need one or the other. */ + s = device_get_property(dev, "sfp", &sc->sfp, sizeof(sc->sfp), + DEVICE_PROP_HANDLE); + s = device_get_property(dev, "phy-handle", &sc->phy_handle, + sizeof(sc->phy_handle), DEVICE_PROP_HANDLE); + + if (bootverbose) + device_printf(dev, "node %#x '%s': reg %#x sfp %#x pcs-handle " + "%#x phy-handle %#x managed '%s' phy-conn-type '%s'\n", + node, ofw_bus_get_name(dev), + sc->reg, sc->sfp, sc->pcs_handle, sc->phy_handle, + sc->managed, sc->phy_conn_type); + + return (0); +} + +static bool +dpaa2_mac_fdt_match_id(device_t dev, uint32_t id) +{ + struct dpaa2_mac_fdt_softc *sc; + + if (dev == NULL) + return (false); + + sc = device_get_softc(dev); + if (sc->reg == id) + return (true); + + return (false); +} + +static device_t +dpaa2_mac_fdt_get_phy_dev(device_t dev) +{ + struct dpaa2_mac_fdt_softc *sc; + + if (dev == NULL) + return (NULL); + + sc = device_get_softc(dev); + if (sc->phy_handle == 0 && sc->sfp == 0) + return (NULL); + +#ifdef __not_yet__ /* No sff,sfp support yet. */ + if (sc->sfp != 0) { + device_t xdev; + + xdev = OF_device_from_xref(OF_xref_from_node(sc->sfp)); + if (xdev != NULL) + return (xdev); + } +#endif + return (OF_device_from_xref(OF_xref_from_node(sc->phy_handle))); +} + +static device_method_t dpaa2_mac_fdt_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, dpaa2_mac_dev_probe), + DEVMETHOD(device_attach, dpaa2_mac_fdt_attach), + DEVMETHOD(device_detach, bus_generic_detach), + + DEVMETHOD_END +}; + +DEFINE_CLASS_0(dpaa2_mac_fdt, dpaa2_mac_fdt_driver, dpaa2_mac_fdt_methods, + sizeof(struct dpaa2_mac_fdt_softc)); +DRIVER_MODULE(dpaa2_mac_fdt, dpaa2_mc, dpaa2_mac_fdt_driver, 0, 0); +MODULE_DEPEND(dpaa2_mac_fdt, memac_mdio_fdt, 1, 1, 1); + +/* + * Device interface. + */ + +static int +dpaa2_mc_fdt_probe(device_t dev) +{ + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_is_compatible(dev, "fsl,qoriq-mc")) + return (ENXIO); + + device_set_desc(dev, "DPAA2 Management Complex"); + return (BUS_PROBE_DEFAULT); +} + +static int +dpaa2_mc_fdt_probe_child(device_t bus, phandle_t child) +{ + device_t childdev; + + /* make sure we do not aliready have a device. */ + childdev = ofw_bus_find_child_device_by_phandle(bus, child); + if (childdev != NULL) + return (0); + + childdev = simplebus_add_device(bus, child, 0, "dpaa2_mac_fdt", -1, + NULL); + if (childdev == NULL) + return (ENXIO); + + return (device_probe_and_attach(childdev)); +} + +static int +dpaa2_mc_fdt_attach(device_t dev) +{ + struct dpaa2_mc_softc *sc; + phandle_t node; + phandle_t child; + + sc = device_get_softc(dev); + sc->acpi_based = false; + sc->ofw_node = ofw_bus_get_node(dev); + + bus_generic_probe(dev); + bus_enumerate_hinted_children(dev); + + bus_generic_probe(dev); + bus_enumerate_hinted_children(dev); + /* + * Attach the children represented in the device tree. + */ + /* fsl-mc -> dpamcs */ + node = OF_child(sc->ofw_node); + simplebus_init(dev, node); + + /* Attach the dpmac children represented in the device tree. */ + child = ofw_bus_find_compatible(node, "fsl,qoriq-mc-dpmac"); + for (; child > 0; child = OF_peer(child)) { + if (!ofw_bus_node_is_compatible(child, "fsl,qoriq-mc-dpmac")) + continue; + if (!OF_hasprop(child, "reg")) + continue; + if (!OF_hasprop(child, "pcs-handle")) + continue; + if (dpaa2_mc_fdt_probe_child(dev, child) != 0) + continue; + } + + return (dpaa2_mc_attach(dev)); +} + +/* + * FDT compat layer. + */ +static device_t +dpaa2_mc_fdt_find_dpaa2_mac_dev(device_t dev, uint32_t id) +{ + int devcount, error, i, len; + device_t *devlist, mdev; + const char *mdevname; + + error = device_get_children(dev, &devlist, &devcount); + if (error != 0) + return (NULL); + + for (i = 0; i < devcount; i++) { + mdev = devlist[i]; + mdevname = device_get_name(mdev); + if (mdevname == NULL) + continue; + len = strlen(mdevname); + if (strncmp("dpaa2_mac_fdt", mdevname, len) != 0) + continue; + if (!device_is_attached(mdev)) + continue; + + if (dpaa2_mac_fdt_match_id(mdev, id)) + return (mdev); + } + + return (NULL); +} + +static int +dpaa2_mc_fdt_get_phy_dev(device_t dev, device_t *phy_dev, uint32_t id) +{ + device_t mdev, pdev; + + mdev = dpaa2_mc_fdt_find_dpaa2_mac_dev(dev, id); + if (mdev == NULL) { + device_printf(dev, "%s: error finding dpmac device with id=%u\n", + __func__, id); + return (ENXIO); + } + + pdev = dpaa2_mac_fdt_get_phy_dev(mdev); + if (pdev == NULL) { + device_printf(dev, "%s: error getting MDIO device for dpamc %s " + "(id=%u)\n", __func__, device_get_nameunit(mdev), id); + return (ENXIO); + } + + if (phy_dev != NULL) + *phy_dev = pdev; + + if (bootverbose) + device_printf(dev, "dpmac_id %u mdev %p (%s) pdev %p (%s)\n", + id, mdev, device_get_nameunit(mdev), + pdev, device_get_nameunit(pdev)); + + return (0); +} + +static const struct ofw_bus_devinfo * +dpaa2_mc_simplebus_get_devinfo(device_t bus, device_t child) +{ + + return (OFW_BUS_GET_DEVINFO(device_get_parent(bus), child)); +} + +static device_method_t dpaa2_mc_fdt_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, dpaa2_mc_fdt_probe), + DEVMETHOD(device_attach, dpaa2_mc_fdt_attach), + DEVMETHOD(device_detach, dpaa2_mc_detach), + + /* Bus interface */ + DEVMETHOD(bus_alloc_resource, dpaa2_mc_alloc_resource), + DEVMETHOD(bus_adjust_resource, dpaa2_mc_adjust_resource), + DEVMETHOD(bus_release_resource, dpaa2_mc_release_resource), + DEVMETHOD(bus_activate_resource, dpaa2_mc_activate_resource), + DEVMETHOD(bus_deactivate_resource, dpaa2_mc_deactivate_resource), + DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), + DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), + + /* Pseudo-PCIB interface */ + DEVMETHOD(pcib_alloc_msi, dpaa2_mc_alloc_msi), + DEVMETHOD(pcib_release_msi, dpaa2_mc_release_msi), + DEVMETHOD(pcib_map_msi, dpaa2_mc_map_msi), + DEVMETHOD(pcib_get_id, dpaa2_mc_get_id), + + /* DPAA2 MC bus interface */ + DEVMETHOD(dpaa2_mc_manage_dev, dpaa2_mc_manage_dev), + DEVMETHOD(dpaa2_mc_get_free_dev,dpaa2_mc_get_free_dev), + DEVMETHOD(dpaa2_mc_get_dev, dpaa2_mc_get_dev), + DEVMETHOD(dpaa2_mc_get_shared_dev, dpaa2_mc_get_shared_dev), + DEVMETHOD(dpaa2_mc_reserve_dev, dpaa2_mc_reserve_dev), + DEVMETHOD(dpaa2_mc_release_dev, dpaa2_mc_release_dev), + DEVMETHOD(dpaa2_mc_get_phy_dev, dpaa2_mc_fdt_get_phy_dev), + + /* OFW/simplebus */ + DEVMETHOD(ofw_bus_get_devinfo, dpaa2_mc_simplebus_get_devinfo), + DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), + DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), + DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), + DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), + DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), + + DEVMETHOD_END +}; + +DEFINE_CLASS_1(dpaa2_mc, dpaa2_mc_fdt_driver, dpaa2_mc_fdt_methods, + sizeof(struct dpaa2_mc_softc), dpaa2_mc_driver); + +DRIVER_MODULE(dpaa2_mc, simplebus, dpaa2_mc_fdt_driver, 0, 0); diff --git a/sys/dev/dpaa2/dpaa2_mc_if.m b/sys/dev/dpaa2/dpaa2_mc_if.m new file mode 100644 index 000000000000..f81d96b5b32f --- /dev/null +++ b/sys/dev/dpaa2/dpaa2_mc_if.m @@ -0,0 +1,152 @@ +#- +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright © 2021-2022 Dmitry Salychev +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +#include +#include + +/** + * @brief Interface of the DPAA2 Management Complex (MC) bus driver. + * + * It helps to manipulate DPAA2-specific resources (DPIOs, DPBPs, etc.) + */ +INTERFACE dpaa2_mc; + +# +# Default implementation of the commands. +# +CODE { + static int + bypass_manage_dev(device_t dev, device_t dpaa2_dev, uint32_t flags) + { + if (device_get_parent(dev) != NULL) + return (DPAA2_MC_MANAGE_DEV(device_get_parent(dev), + dpaa2_dev, flags)); + return (ENXIO); + } + + static int + bypass_get_free_dev(device_t dev, device_t *dpaa2_dev, + enum dpaa2_dev_type devtype) + { + if (device_get_parent(dev) != NULL) + return (DPAA2_MC_GET_FREE_DEV(device_get_parent(dev), + dpaa2_dev, devtype)); + return (ENXIO); + } + + static int + bypass_get_dev(device_t dev, device_t *dpaa2_dev, + enum dpaa2_dev_type devtype, uint32_t obj_id) + { + if (device_get_parent(dev) != NULL) + return (DPAA2_MC_GET_DEV(device_get_parent(dev), + dpaa2_dev, devtype, obj_id)); + return (ENXIO); + } + + static int + bypass_get_shared_dev(device_t dev, device_t *dpaa2_dev, + enum dpaa2_dev_type devtype) + { + if (device_get_parent(dev) != NULL) + return (DPAA2_MC_GET_SHARED_DEV(device_get_parent(dev), + dpaa2_dev, devtype)); + return (ENXIO); + } + + static int + bypass_reserve_dev(device_t dev, device_t dpaa2_dev, + enum dpaa2_dev_type devtype) + { + if (device_get_parent(dev) != NULL) + return (DPAA2_MC_RESERVE_DEV(device_get_parent(dev), + dpaa2_dev, devtype)); + return (ENXIO); + } + + static int + bypass_release_dev(device_t dev, device_t dpaa2_dev, + enum dpaa2_dev_type devtype) + { + if (device_get_parent(dev) != NULL) + return (DPAA2_MC_RELEASE_DEV(device_get_parent(dev), + dpaa2_dev, devtype)); + return (ENXIO); + } + + static int + bypass_get_phy_dev(device_t dev, device_t *phy_dev, uint32_t id) + { + if (device_get_parent(dev) != NULL) + return (DPAA2_MC_GET_PHY_DEV(device_get_parent(dev), + phy_dev, id)); + return (ENXIO); + } +} + +METHOD int manage_dev { + device_t dev; + device_t dpaa2_dev; + uint32_t flags; +} DEFAULT bypass_manage_dev; + +METHOD int get_free_dev { + device_t dev; + device_t *dpaa2_dev; + enum dpaa2_dev_type devtype; +} DEFAULT bypass_get_free_dev; + +METHOD int get_dev { + device_t dev; + device_t *dpaa2_dev; + enum dpaa2_dev_type devtype; + uint32_t obj_id; +} DEFAULT bypass_get_dev; + +METHOD int get_shared_dev { + device_t dev; + device_t *dpaa2_dev; + enum dpaa2_dev_type devtype; +} DEFAULT bypass_get_shared_dev; + +METHOD int reserve_dev { + device_t dev; + device_t dpaa2_dev; + enum dpaa2_dev_type devtype; +} DEFAULT bypass_reserve_dev; + +METHOD int release_dev { + device_t dev; + device_t dpaa2_dev; + enum dpaa2_dev_type devtype; +} DEFAULT bypass_release_dev; + +METHOD int get_phy_dev { + device_t dev; + device_t *phy_dev; + uint32_t id; +} DEFAULT bypass_get_phy_dev; diff --git a/sys/dev/dpaa2/dpaa2_mcp.c b/sys/dev/dpaa2/dpaa2_mcp.c new file mode 100644 index 000000000000..f41d9a7d21b0 --- /dev/null +++ b/sys/dev/dpaa2/dpaa2_mcp.c @@ -0,0 +1,318 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright © 2021-2022 Dmitry Salychev + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +/* + * DPAA2 MC command portal and helper routines. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "pcib_if.h" +#include "pci_if.h" + +#include "dpaa2_mcp.h" +#include "dpaa2_mc.h" +#include "dpaa2_cmd_if.h" + +MALLOC_DEFINE(M_DPAA2_MCP, "dpaa2_mcp", "DPAA2 Management Complex Portal"); + +static struct resource_spec dpaa2_mcp_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE | RF_UNMAPPED }, + RESOURCE_SPEC_END +}; + +int +dpaa2_mcp_init_portal(struct dpaa2_mcp **mcp, struct resource *res, + struct resource_map *map, uint16_t flags) +{ + const int mflags = flags & DPAA2_PORTAL_NOWAIT_ALLOC + ? (M_NOWAIT | M_ZERO) : (M_WAITOK | M_ZERO); + struct dpaa2_mcp *p; + + if (!mcp || !res || !map) + return (DPAA2_CMD_STAT_EINVAL); + + p = malloc(sizeof(struct dpaa2_mcp), M_DPAA2_MCP, mflags); + if (p == NULL) + return (DPAA2_CMD_STAT_NO_MEMORY); + + mtx_init(&p->lock, "mcp_sleep_lock", NULL, MTX_DEF); + + p->res = res; + p->map = map; + p->flags = flags; + p->rc_api_major = 0; /* DPRC API version to be cached later. */ + p->rc_api_minor = 0; + + *mcp = p; + + return (0); +} + +void +dpaa2_mcp_free_portal(struct dpaa2_mcp *mcp) +{ + uint16_t flags; + + KASSERT(mcp != NULL, ("%s: mcp is NULL", __func__)); + + DPAA2_MCP_LOCK(mcp, &flags); + mcp->flags |= DPAA2_PORTAL_DESTROYED; + DPAA2_MCP_UNLOCK(mcp); + + /* Let threads stop using this portal. */ + DELAY(DPAA2_PORTAL_TIMEOUT); + + mtx_destroy(&mcp->lock); + free(mcp, M_DPAA2_MCP); +} + +int +dpaa2_mcp_init_command(struct dpaa2_cmd **cmd, uint16_t flags) +{ + const int mflags = flags & DPAA2_CMD_NOWAIT_ALLOC + ? (M_NOWAIT | M_ZERO) : (M_WAITOK | M_ZERO); + struct dpaa2_cmd *c; + struct dpaa2_cmd_header *hdr; + + if (!cmd) + return (DPAA2_CMD_STAT_EINVAL); + + c = malloc(sizeof(struct dpaa2_cmd), M_DPAA2_MCP, mflags); + if (!c) + return (DPAA2_CMD_STAT_NO_MEMORY); + + hdr = (struct dpaa2_cmd_header *) &c->header; + hdr->srcid = 0; + hdr->status = DPAA2_CMD_STAT_OK; + hdr->token = 0; + hdr->cmdid = 0; + hdr->flags_hw = DPAA2_CMD_DEF; + hdr->flags_sw = DPAA2_CMD_DEF; + if (flags & DPAA2_CMD_HIGH_PRIO) + hdr->flags_hw |= DPAA2_HW_FLAG_HIGH_PRIO; + if (flags & DPAA2_CMD_INTR_DIS) + hdr->flags_sw |= DPAA2_SW_FLAG_INTR_DIS; + for (uint32_t i = 0; i < DPAA2_CMD_PARAMS_N; i++) + c->params[i] = 0; + *cmd = c; + + return (0); +} + +void +dpaa2_mcp_free_command(struct dpaa2_cmd *cmd) +{ + if (cmd != NULL) + free(cmd, M_DPAA2_MCP); +} + +struct dpaa2_cmd * +dpaa2_mcp_tk(struct dpaa2_cmd *cmd, uint16_t token) +{ + struct dpaa2_cmd_header *hdr; + if (cmd != NULL) { + hdr = (struct dpaa2_cmd_header *) &cmd->header; + hdr->token = token; + } + return (cmd); +} + +struct dpaa2_cmd * +dpaa2_mcp_f(struct dpaa2_cmd *cmd, uint16_t flags) +{ + struct dpaa2_cmd_header *hdr; + if (cmd) { + hdr = (struct dpaa2_cmd_header *) &cmd->header; + hdr->flags_hw = DPAA2_CMD_DEF; + hdr->flags_sw = DPAA2_CMD_DEF; + + if (flags & DPAA2_CMD_HIGH_PRIO) + hdr->flags_hw |= DPAA2_HW_FLAG_HIGH_PRIO; + if (flags & DPAA2_CMD_INTR_DIS) + hdr->flags_sw |= DPAA2_SW_FLAG_INTR_DIS; + } + return (cmd); +} + +static int +dpaa2_mcp_probe(device_t dev) +{ + /* DPMCP device will be added by the parent resource container. */ + device_set_desc(dev, "DPAA2 MC portal"); + return (BUS_PROBE_DEFAULT); +} + +static int +dpaa2_mcp_detach(device_t dev) +{ + return (0); +} + +static int +dpaa2_mcp_attach(device_t dev) +{ + device_t pdev = device_get_parent(dev); + device_t child = dev; + struct dpaa2_mcp_softc *sc = device_get_softc(dev); + struct dpaa2_devinfo *rcinfo = device_get_ivars(pdev); + struct dpaa2_devinfo *dinfo = device_get_ivars(dev); + struct dpaa2_cmd *cmd; + struct dpaa2_mcp *portal; + struct resource_map_request req; + uint16_t rc_token, mcp_token; + int error; + + sc->dev = dev; + + error = bus_alloc_resources(sc->dev, dpaa2_mcp_spec, sc->res); + if (error) { + device_printf(dev, "%s: failed to allocate resources\n", + __func__); + goto err_exit; + } + + /* At least 64 bytes of the command portal should be available. */ + if (rman_get_size(sc->res[0]) < DPAA2_MCP_MEM_WIDTH) { + device_printf(dev, "%s: MC portal memory region too small: " + "%jd\n", __func__, rman_get_size(sc->res[0])); + goto err_exit; + } + + /* Map MC portal memory resource. */ + resource_init_map_request(&req); + req.memattr = VM_MEMATTR_DEVICE; + error = bus_map_resource(sc->dev, SYS_RES_MEMORY, sc->res[0], &req, + &sc->map[0]); + if (error) { + device_printf(dev, "%s: failed to map MC portal memory\n", + __func__); + goto err_exit; + } + + /* Initialize portal to send commands to MC. */ + error = dpaa2_mcp_init_portal(&portal, sc->res[0], &sc->map[0], + DPAA2_PORTAL_DEF); + if (error) { + device_printf(dev, "%s: failed to initialize dpaa2_mcp: " + "error=%d\n", __func__, error); + goto err_exit; + } + + /* Allocate a command to send to MC hardware. */ + error = dpaa2_mcp_init_command(&cmd, DPAA2_CMD_DEF); + if (error) { + device_printf(dev, "%s: failed to allocate dpaa2_cmd: " + "error=%d\n", __func__, error); + goto err_exit; + } + + /* Open resource container and DPMCP object. */ + error = DPAA2_CMD_RC_OPEN(dev, child, cmd, rcinfo->id, &rc_token); + if (error) { + device_printf(dev, "%s: failed to open DPRC: error=%d\n", + __func__, error); + goto err_free_cmd; + } + error = DPAA2_CMD_MCP_OPEN(dev, child, cmd, dinfo->id, &mcp_token); + if (error) { + device_printf(dev, "%s: failed to open DPMCP: id=%d, error=%d\n", + __func__, dinfo->id, error); + goto err_close_rc; + } + + /* Prepare DPMCP object. */ + error = DPAA2_CMD_MCP_RESET(dev, child, cmd); + if (error) { + device_printf(dev, "%s: failed to reset DPMCP: id=%d, " + "error=%d\n", __func__, dinfo->id, error); + goto err_close_mcp; + } + + /* Close the DPMCP object and the resource container. */ + error = DPAA2_CMD_MCP_CLOSE(dev, child, cmd); + if (error) { + device_printf(dev, "%s: failed to close DPMCP: id=%d, " + "error=%d\n", __func__, dinfo->id, error); + goto err_close_rc; + } + error = DPAA2_CMD_RC_CLOSE(dev, child, dpaa2_mcp_tk(cmd, rc_token)); + if (error) { + device_printf(dev, "%s: failed to close DPRC: error=%d\n", + __func__, error); + goto err_free_cmd; + } + + dpaa2_mcp_free_command(cmd); + dinfo->portal = portal; + + return (0); + +err_close_mcp: + DPAA2_CMD_MCP_CLOSE(dev, child, dpaa2_mcp_tk(cmd, mcp_token)); +err_close_rc: + DPAA2_CMD_RC_CLOSE(dev, child, dpaa2_mcp_tk(cmd, rc_token)); +err_free_cmd: + dpaa2_mcp_free_command(cmd); +err_exit: + dpaa2_mcp_detach(dev); + return (ENXIO); +} + +static device_method_t dpaa2_mcp_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, dpaa2_mcp_probe), + DEVMETHOD(device_attach, dpaa2_mcp_attach), + DEVMETHOD(device_detach, dpaa2_mcp_detach), + + DEVMETHOD_END +}; + +static driver_t dpaa2_mcp_driver = { + "dpaa2_mcp", + dpaa2_mcp_methods, + sizeof(struct dpaa2_mcp_softc), +}; + +DRIVER_MODULE(dpaa2_mcp, dpaa2_rc, dpaa2_mcp_driver, 0, 0); diff --git a/sys/dev/dpaa2/dpaa2_mcp.h b/sys/dev/dpaa2/dpaa2_mcp.h new file mode 100644 index 000000000000..55052ca7afb2 --- /dev/null +++ b/sys/dev/dpaa2/dpaa2_mcp.h @@ -0,0 +1,449 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright © 2021-2022 Dmitry Salychev + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _DPAA2_MCP_H +#define _DPAA2_MCP_H + +#include +#include +#include + +#include "dpaa2_types.h" + +/* + * DPAA2 MC command interface helper routines. + */ + +#define DPAA2_PORTAL_TIMEOUT 100000 /* us */ +#define DPAA2_MCP_MEM_WIDTH 0x40 /* Minimal size of the MC portal. */ +#define DPAA2_MCP_MAX_RESOURCES 1 /* resources per DPMCP: 1 SYS_MEM */ + +/* + * Portal flags. + * + * TODO: Use the same flags for both MC and software portals. + */ +#define DPAA2_PORTAL_DEF 0x0u +#define DPAA2_PORTAL_NOWAIT_ALLOC 0x2u /* Do not sleep during init */ +#define DPAA2_PORTAL_LOCKED 0x4000u /* Wait till portal's unlocked */ +#define DPAA2_PORTAL_DESTROYED 0x8000u /* Terminate any operations */ + +/* Command flags. */ +#define DPAA2_CMD_DEF 0x0u +#define DPAA2_CMD_HIGH_PRIO 0x80u /* High priority command */ +#define DPAA2_CMD_INTR_DIS 0x100u /* Disable cmd finished intr */ +#define DPAA2_CMD_NOWAIT_ALLOC 0x8000u /* Do not sleep during init */ + +/* DPAA2 command return codes. */ +#define DPAA2_CMD_STAT_OK 0x0 /* Set by MC on success */ +#define DPAA2_CMD_STAT_READY 0x1 /* Ready to be processed */ +#define DPAA2_CMD_STAT_AUTH_ERR 0x3 /* Illegal object-portal-icid */ +#define DPAA2_CMD_STAT_NO_PRIVILEGE 0x4 /* No privilege */ +#define DPAA2_CMD_STAT_DMA_ERR 0x5 /* DMA or I/O error */ +#define DPAA2_CMD_STAT_CONFIG_ERR 0x6 /* Invalid/conflicting params */ +#define DPAA2_CMD_STAT_TIMEOUT 0x7 /* Command timed out */ +#define DPAA2_CMD_STAT_NO_RESOURCE 0x8 /* No DPAA2 resources */ +#define DPAA2_CMD_STAT_NO_MEMORY 0x9 /* No memory available */ +#define DPAA2_CMD_STAT_BUSY 0xA /* Device is busy */ +#define DPAA2_CMD_STAT_UNSUPPORTED_OP 0xB /* Unsupported operation */ +#define DPAA2_CMD_STAT_INVALID_STATE 0xC /* Invalid state */ +/* Driver-specific return codes. */ +#define DPAA2_CMD_STAT_UNKNOWN_OBJ 0xFD /* Unknown DPAA2 object. */ +#define DPAA2_CMD_STAT_EINVAL 0xFE /* Invalid argument */ +#define DPAA2_CMD_STAT_ERR 0xFF /* General error */ + +/* Object's memory region flags. */ +#define DPAA2_RC_REG_CACHEABLE 0x1 /* Cacheable memory mapping */ + +#define DPAA2_HW_FLAG_HIGH_PRIO 0x80u +#define DPAA2_SW_FLAG_INTR_DIS 0x01u + +#define DPAA2_CMD_PARAMS_N 7u +#define DPAA2_LABEL_SZ 16 + +/* ------------------------- MNG command IDs -------------------------------- */ +#define CMD_MNG_BASE_VERSION 1 +#define CMD_MNG_ID_OFFSET 4 + +#define CMD_MNG(id) (((id) << CMD_MNG_ID_OFFSET) | CMD_MNG_BASE_VERSION) + +#define CMDID_MNG_GET_VER CMD_MNG(0x831) +#define CMDID_MNG_GET_SOC_VER CMD_MNG(0x832) +#define CMDID_MNG_GET_CONT_ID CMD_MNG(0x830) + +/* ------------------------- DPRC command IDs ------------------------------- */ +#define CMD_RC_BASE_VERSION 1 +#define CMD_RC_2ND_VERSION 2 +#define CMD_RC_3RD_VERSION 3 +#define CMD_RC_ID_OFFSET 4 + +#define CMD_RC(id) (((id) << CMD_RC_ID_OFFSET) | CMD_RC_BASE_VERSION) +#define CMD_RC_V2(id) (((id) << CMD_RC_ID_OFFSET) | CMD_RC_2ND_VERSION) +#define CMD_RC_V3(id) (((id) << CMD_RC_ID_OFFSET) | CMD_RC_3RD_VERSION) + +#define CMDID_RC_OPEN CMD_RC(0x805) +#define CMDID_RC_CLOSE CMD_RC(0x800) +#define CMDID_RC_GET_API_VERSION CMD_RC(0xA05) +#define CMDID_RC_GET_ATTR CMD_RC(0x004) +#define CMDID_RC_RESET_CONT CMD_RC(0x005) +#define CMDID_RC_RESET_CONT_V2 CMD_RC_V2(0x005) +#define CMDID_RC_SET_IRQ CMD_RC(0x010) +#define CMDID_RC_SET_IRQ_ENABLE CMD_RC(0x012) +#define CMDID_RC_SET_IRQ_MASK CMD_RC(0x014) +#define CMDID_RC_GET_IRQ_STATUS CMD_RC(0x016) +#define CMDID_RC_CLEAR_IRQ_STATUS CMD_RC(0x017) +#define CMDID_RC_GET_CONT_ID CMD_RC(0x830) +#define CMDID_RC_GET_OBJ_COUNT CMD_RC(0x159) +#define CMDID_RC_GET_OBJ CMD_RC(0x15A) +#define CMDID_RC_GET_OBJ_DESC CMD_RC(0x162) +#define CMDID_RC_GET_OBJ_REG CMD_RC(0x15E) +#define CMDID_RC_GET_OBJ_REG_V2 CMD_RC_V2(0x15E) +#define CMDID_RC_GET_OBJ_REG_V3 CMD_RC_V3(0x15E) +#define CMDID_RC_SET_OBJ_IRQ CMD_RC(0x15F) +#define CMDID_RC_GET_CONN CMD_RC(0x16C) + +/* ------------------------- DPIO command IDs ------------------------------- */ +#define CMD_IO_BASE_VERSION 1 +#define CMD_IO_ID_OFFSET 4 + +#define CMD_IO(id) (((id) << CMD_IO_ID_OFFSET) | CMD_IO_BASE_VERSION) + +#define CMDID_IO_OPEN CMD_IO(0x803) +#define CMDID_IO_CLOSE CMD_IO(0x800) +#define CMDID_IO_ENABLE CMD_IO(0x002) +#define CMDID_IO_DISABLE CMD_IO(0x003) +#define CMDID_IO_GET_ATTR CMD_IO(0x004) +#define CMDID_IO_RESET CMD_IO(0x005) +#define CMDID_IO_SET_IRQ_ENABLE CMD_IO(0x012) +#define CMDID_IO_SET_IRQ_MASK CMD_IO(0x014) +#define CMDID_IO_GET_IRQ_STATUS CMD_IO(0x016) +#define CMDID_IO_ADD_STATIC_DQ_CHAN CMD_IO(0x122) + +/* ------------------------- DPNI command IDs ------------------------------- */ +#define CMD_NI_BASE_VERSION 1 +#define CMD_NI_2ND_VERSION 2 +#define CMD_NI_4TH_VERSION 4 +#define CMD_NI_ID_OFFSET 4 + +#define CMD_NI(id) (((id) << CMD_NI_ID_OFFSET) | CMD_NI_BASE_VERSION) +#define CMD_NI_V2(id) (((id) << CMD_NI_ID_OFFSET) | CMD_NI_2ND_VERSION) +#define CMD_NI_V4(id) (((id) << CMD_NI_ID_OFFSET) | CMD_NI_4TH_VERSION) + +#define CMDID_NI_OPEN CMD_NI(0x801) +#define CMDID_NI_CLOSE CMD_NI(0x800) +#define CMDID_NI_ENABLE CMD_NI(0x002) +#define CMDID_NI_DISABLE CMD_NI(0x003) +#define CMDID_NI_GET_API_VER CMD_NI(0xA01) +#define CMDID_NI_RESET CMD_NI(0x005) +#define CMDID_NI_GET_ATTR CMD_NI(0x004) +#define CMDID_NI_SET_BUF_LAYOUT CMD_NI(0x265) +#define CMDID_NI_GET_TX_DATA_OFF CMD_NI(0x212) +#define CMDID_NI_GET_PORT_MAC_ADDR CMD_NI(0x263) +#define CMDID_NI_SET_PRIM_MAC_ADDR CMD_NI(0x224) +#define CMDID_NI_GET_PRIM_MAC_ADDR CMD_NI(0x225) +#define CMDID_NI_SET_LINK_CFG CMD_NI(0x21A) +#define CMDID_NI_GET_LINK_CFG CMD_NI(0x278) +#define CMDID_NI_GET_LINK_STATE CMD_NI(0x215) +#define CMDID_NI_SET_QOS_TABLE CMD_NI(0x240) +#define CMDID_NI_CLEAR_QOS_TABLE CMD_NI(0x243) +#define CMDID_NI_SET_POOLS CMD_NI(0x200) +#define CMDID_NI_SET_ERR_BEHAVIOR CMD_NI(0x20B) +#define CMDID_NI_GET_QUEUE CMD_NI(0x25F) +#define CMDID_NI_SET_QUEUE CMD_NI(0x260) +#define CMDID_NI_GET_QDID CMD_NI(0x210) +#define CMDID_NI_ADD_MAC_ADDR CMD_NI(0x226) +#define CMDID_NI_REMOVE_MAC_ADDR CMD_NI(0x227) +#define CMDID_NI_CLEAR_MAC_FILTERS CMD_NI(0x228) +#define CMDID_NI_SET_MFL CMD_NI(0x216) +#define CMDID_NI_SET_OFFLOAD CMD_NI(0x26C) +#define CMDID_NI_SET_IRQ_MASK CMD_NI(0x014) +#define CMDID_NI_SET_IRQ_ENABLE CMD_NI(0x012) +#define CMDID_NI_GET_IRQ_STATUS CMD_NI(0x016) +#define CMDID_NI_SET_UNI_PROMISC CMD_NI(0x222) +#define CMDID_NI_SET_MULTI_PROMISC CMD_NI(0x220) +#define CMDID_NI_GET_STATISTICS CMD_NI(0x25D) +#define CMDID_NI_SET_RX_TC_DIST CMD_NI(0x235) + +/* ------------------------- DPBP command IDs ------------------------------- */ +#define CMD_BP_BASE_VERSION 1 +#define CMD_BP_ID_OFFSET 4 + +#define CMD_BP(id) (((id) << CMD_BP_ID_OFFSET) | CMD_BP_BASE_VERSION) + +#define CMDID_BP_OPEN CMD_BP(0x804) +#define CMDID_BP_CLOSE CMD_BP(0x800) +#define CMDID_BP_ENABLE CMD_BP(0x002) +#define CMDID_BP_DISABLE CMD_BP(0x003) +#define CMDID_BP_GET_ATTR CMD_BP(0x004) +#define CMDID_BP_RESET CMD_BP(0x005) + +/* ------------------------- DPMAC command IDs ------------------------------ */ +#define CMD_MAC_BASE_VERSION 1 +#define CMD_MAC_2ND_VERSION 2 +#define CMD_MAC_ID_OFFSET 4 + +#define CMD_MAC(id) (((id) << CMD_MAC_ID_OFFSET) | CMD_MAC_BASE_VERSION) +#define CMD_MAC_V2(id) (((id) << CMD_MAC_ID_OFFSET) | CMD_MAC_2ND_VERSION) + +#define CMDID_MAC_OPEN CMD_MAC(0x80C) +#define CMDID_MAC_CLOSE CMD_MAC(0x800) +#define CMDID_MAC_RESET CMD_MAC(0x005) +#define CMDID_MAC_MDIO_READ CMD_MAC(0x0C0) +#define CMDID_MAC_MDIO_WRITE CMD_MAC(0x0C1) +#define CMDID_MAC_GET_ADDR CMD_MAC(0x0C5) +#define CMDID_MAC_GET_ATTR CMD_MAC(0x004) +#define CMDID_MAC_SET_LINK_STATE CMD_MAC_V2(0x0C3) +#define CMDID_MAC_SET_IRQ_MASK CMD_MAC(0x014) +#define CMDID_MAC_SET_IRQ_ENABLE CMD_MAC(0x012) +#define CMDID_MAC_GET_IRQ_STATUS CMD_MAC(0x016) + +/* ------------------------- DPCON command IDs ------------------------------ */ +#define CMD_CON_BASE_VERSION 1 +#define CMD_CON_ID_OFFSET 4 + +#define CMD_CON(id) (((id) << CMD_CON_ID_OFFSET) | CMD_CON_BASE_VERSION) + +#define CMDID_CON_OPEN CMD_CON(0x808) +#define CMDID_CON_CLOSE CMD_CON(0x800) +#define CMDID_CON_ENABLE CMD_CON(0x002) +#define CMDID_CON_DISABLE CMD_CON(0x003) +#define CMDID_CON_GET_ATTR CMD_CON(0x004) +#define CMDID_CON_RESET CMD_CON(0x005) +#define CMDID_CON_SET_NOTIF CMD_CON(0x100) + +/* ------------------------- DPMCP command IDs ------------------------------ */ +#define CMD_MCP_BASE_VERSION 1 +#define CMD_MCP_2ND_VERSION 2 +#define CMD_MCP_ID_OFFSET 4 + +#define CMD_MCP(id) (((id) << CMD_MCP_ID_OFFSET) | CMD_MCP_BASE_VERSION) +#define CMD_MCP_V2(id) (((id) << CMD_MCP_ID_OFFSET) | CMD_MCP_2ND_VERSION) + +#define CMDID_MCP_CREATE CMD_MCP_V2(0x90B) +#define CMDID_MCP_DESTROY CMD_MCP(0x98B) +#define CMDID_MCP_OPEN CMD_MCP(0x80B) +#define CMDID_MCP_CLOSE CMD_MCP(0x800) +#define CMDID_MCP_RESET CMD_MCP(0x005) + +#define DPAA2_MCP_LOCK(__mcp, __flags) do { \ + mtx_assert(&(__mcp)->lock, MA_NOTOWNED); \ + mtx_lock(&(__mcp)->lock); \ + *(__flags) = (__mcp)->flags; \ + (__mcp)->flags |= DPAA2_PORTAL_LOCKED; \ +} while (0) + +#define DPAA2_MCP_UNLOCK(__mcp) do { \ + mtx_assert(&(__mcp)->lock, MA_OWNED); \ + (__mcp)->flags &= ~DPAA2_PORTAL_LOCKED; \ + mtx_unlock(&(__mcp)->lock); \ +} while (0) + +enum dpaa2_rc_region_type { + DPAA2_RC_REG_MC_PORTAL, + DPAA2_RC_REG_QBMAN_PORTAL +}; + +/** + * @brief Helper object to interact with the MC portal. + * + * res: Unmapped portal's I/O memory. + * map: Mapped portal's I/O memory. + * lock: Lock to send a command to the portal and wait for the + * result. + * flags: Current state of the object. + * rc_api_major: Major version of the DPRC API. + * rc_api_minor: Minor version of the DPRC API. + */ +struct dpaa2_mcp { + struct resource *res; + struct resource_map *map; + struct mtx lock; + uint16_t flags; + uint16_t rc_api_major; + uint16_t rc_api_minor; +}; + +/** + * @brief Command object holds data to be written to the MC portal. + * + * header: 8 least significant bytes of the MC portal. + * params: Parameters to pass together with the command to MC. Might keep + * command execution results. + * + * NOTE: 64 bytes. + */ +struct dpaa2_cmd { + uint64_t header; + uint64_t params[DPAA2_CMD_PARAMS_N]; +}; + +/** + * @brief Helper object to access fields of the MC command header. + * + * srcid: The SoC architected source ID of the submitter. This field is + * reserved and cannot be written by the driver. + * flags_hw: Bits from 8 to 15 of the command header. Most of them are + * reserved at the moment. + * status: Command ready/status. This field is used as the handshake field + * between MC and the driver. MC reports command completion with + * success/error codes in this field. + * flags_sw: ... + * token: ... + * cmdid: ... + * + * NOTE: 8 bytes. + */ +struct dpaa2_cmd_header { + uint8_t srcid; + uint8_t flags_hw; + uint8_t status; + uint8_t flags_sw; + uint16_t token; + uint16_t cmdid; +} __packed; + +/** + * @brief Information about DPAA2 object. + * + * id: ID of a logical object resource. + * vendor: Object vendor identifier. + * irq_count: Number of interrupts supported by the object. + * reg_count: Number of mappable regions supported by the object. + * state: Object state (combination of states). + * ver_major: Major version of the object. + * ver_minor: Minor version of the object. + * flags: Object attributes flags. + * type: ... + * label: ... + */ +struct dpaa2_obj { + uint32_t id; + uint16_t vendor; + uint8_t irq_count; + uint8_t reg_count; + uint32_t state; + uint16_t ver_major; + uint16_t ver_minor; + uint16_t flags; + uint8_t label[DPAA2_LABEL_SZ]; + enum dpaa2_dev_type type; +}; + +/** + * @brief Attributes of the DPRC object. + * + * cont_id: Container ID. + * portal_id: Container's portal ID. + * options: Container's options as set at container's creation. + * icid: Container's isolation context ID. + */ +struct dpaa2_rc_attr { + uint32_t cont_id; + uint32_t portal_id; + uint32_t options; + uint32_t icid; +}; + +/** + * @brief Description of the object's memory region. + * + * base_paddr: Region base physical address. + * base_offset: Region base offset. + * size: Region size (in bytes). + * flags: Region flags (cacheable, etc.) + * type: Type of a software portal this region belongs to. + */ +struct dpaa2_rc_obj_region { + uint64_t base_paddr; + uint64_t base_offset; + uint32_t size; + uint32_t flags; + enum dpaa2_rc_region_type type; +}; + +/** + * @brief DPAA2 endpoint descriptor. + * + * obj_id: Endpoint object ID. + * if_id: Interface ID; for endpoints with multiple interfaces + * (DPSW, DPDMUX), 0 - otherwise. + * type: Endpoint object type, null-terminated string. + */ +struct dpaa2_ep_desc { + uint32_t obj_id; + uint32_t if_id; + enum dpaa2_dev_type type; +}; + +/** + * @brief Configuration of the channel data availability notification (CDAN). + * + * qman_ctx: Context value provided with each CDAN message. + * dpio_id: DPIO object ID configured with a notification channel. + * prior: Priority selection within the DPIO channel; valid values + * are 0-7, depending on the number of priorities in that channel. + */ +struct dpaa2_con_notif_cfg { + uint64_t qman_ctx; + uint32_t dpio_id; + uint8_t prior; +}; + +/** + * @brief Attributes of the DPMCP object. + * + * id: DPMCP object ID. + * options: Options of the MC portal (disabled high-prio commands, etc.). + */ +struct dpaa2_mcp_attr { + uint32_t id; + uint32_t options; +}; + +/** + * @brief Software context for the DPAA2 MC portal. + */ +struct dpaa2_mcp_softc { + device_t dev; + struct dpaa2_mcp_attr attr; + + struct resource *res[DPAA2_MCP_MAX_RESOURCES]; + struct resource_map map[DPAA2_MCP_MAX_RESOURCES]; +}; + +int dpaa2_mcp_init_portal(struct dpaa2_mcp **mcp, struct resource *res, + struct resource_map *map, uint16_t flags); +int dpaa2_mcp_init_command(struct dpaa2_cmd **cmd, uint16_t flags); +void dpaa2_mcp_free_portal(struct dpaa2_mcp *mcp); +void dpaa2_mcp_free_command(struct dpaa2_cmd *cmd); + +/* to quickly update command token */ +struct dpaa2_cmd *dpaa2_mcp_tk(struct dpaa2_cmd *cmd, uint16_t token); +/* to quickly update command flags */ +struct dpaa2_cmd *dpaa2_mcp_f(struct dpaa2_cmd *cmd, uint16_t flags); + +#endif /* _DPAA2_MCP_H */ diff --git a/sys/dev/dpaa2/dpaa2_ni.c b/sys/dev/dpaa2/dpaa2_ni.c new file mode 100644 index 000000000000..f62d6c1b6f29 --- /dev/null +++ b/sys/dev/dpaa2/dpaa2_ni.c @@ -0,0 +1,3670 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright © 2021-2022 Dmitry Salychev + * Copyright © 2022 Mathew McBride + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +/* + * The DPAA2 Network Interface (DPNI) driver. + * + * The DPNI object is a network interface that is configurable to support a wide + * range of features from a very basic Ethernet interface up to a + * high-functioning network interface. The DPNI supports features that are + * expected by standard network stacks, from basic features to offloads. + * + * DPNIs work with Ethernet traffic, starting with the L2 header. Additional + * functions are provided for standard network protocols (L2, L3, L4, etc.). + */ + +#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 +#include +#include + +#include "opt_acpi.h" +#include "opt_platform.h" + +#include "pcib_if.h" +#include "pci_if.h" +#include "miibus_if.h" +#include "memac_mdio_if.h" + +#include "dpaa2_types.h" +#include "dpaa2_mc.h" +#include "dpaa2_mc_if.h" +#include "dpaa2_mcp.h" +#include "dpaa2_swp.h" +#include "dpaa2_swp_if.h" +#include "dpaa2_cmd_if.h" +#include "dpaa2_ni.h" + +#define BIT(x) (1ul << (x)) +#define WRIOP_VERSION(x, y, z) ((x) << 10 | (y) << 5 | (z) << 0) +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + +/* Frame Dequeue Response status bits. */ +#define IS_NULL_RESPONSE(stat) ((((stat) >> 4) & 1) == 0) + +#define ALIGN_UP(x, y) roundup2((x), (y)) +#define ALIGN_DOWN(x, y) rounddown2((x), (y)) +#define CACHE_LINE_ALIGN(x) ALIGN_UP((x), CACHE_LINE_SIZE) + +#define DPNI_LOCK(__sc) do { \ + mtx_assert(&(__sc)->lock, MA_NOTOWNED); \ + mtx_lock(&(__sc)->lock); \ +} while (0) +#define DPNI_UNLOCK(__sc) do { \ + mtx_assert(&(__sc)->lock, MA_OWNED); \ + mtx_unlock(&(__sc)->lock); \ +} while (0) + +#define TX_LOCK(__tx) do { \ + mtx_assert(&(__tx)->lock, MA_NOTOWNED); \ + mtx_lock(&(__tx)->lock); \ +} while (0) +#define TX_UNLOCK(__tx) do { \ + mtx_assert(&(__tx)->lock, MA_OWNED); \ + mtx_unlock(&(__tx)->lock); \ +} while (0) + +#define DPAA2_TX_RING(sc, chan, tc) \ + (&(sc)->channels[(chan)]->txc_queue.tx_rings[(tc)]) + +#define DPNI_IRQ_INDEX 0 /* Index of the only DPNI IRQ. */ +#define DPNI_IRQ_LINK_CHANGED 1 /* Link state changed */ +#define DPNI_IRQ_EP_CHANGED 2 /* DPAA2 endpoint dis/connected */ + +/* Default maximum frame length. */ +#define DPAA2_ETH_MFL (ETHER_MAX_LEN - ETHER_CRC_LEN) + +/* Minimally supported version of the DPNI API. */ +#define DPNI_VER_MAJOR 7 +#define DPNI_VER_MINOR 0 + +/* Rx/Tx buffers configuration. */ +#define BUF_ALIGN_V1 256 /* WRIOP v1.0.0 limitation */ +#define BUF_ALIGN 64 +#define BUF_SWA_SIZE 64 /* SW annotation size */ +#define BUF_RX_HWA_SIZE 64 /* HW annotation size */ +#define BUF_TX_HWA_SIZE 128 /* HW annotation size */ +#define BUF_SIZE (MJUM9BYTES) +#define BUF_MAXADDR_49BIT 0x1FFFFFFFFFFFFul +#define BUF_MAXADDR (BUS_SPACE_MAXADDR) + +#define DPAA2_TX_BUFRING_SZ (4096u) +#define DPAA2_TX_SEGLIMIT (16u) /* arbitrary number */ +#define DPAA2_TX_SEG_SZ (4096u) +#define DPAA2_TX_SEGS_MAXSZ (DPAA2_TX_SEGLIMIT * DPAA2_TX_SEG_SZ) +#define DPAA2_TX_SGT_SZ (512u) /* bytes */ + +/* Size of a buffer to keep a QoS table key configuration. */ +#define ETH_QOS_KCFG_BUF_SIZE 256 + +/* Required by struct dpni_rx_tc_dist_cfg::key_cfg_iova */ +#define DPAA2_CLASSIFIER_DMA_SIZE 256 + +/* Channel storage buffer configuration. */ +#define ETH_STORE_FRAMES 16u +#define ETH_STORE_SIZE ((ETH_STORE_FRAMES + 1) * sizeof(struct dpaa2_dq)) +#define ETH_STORE_ALIGN 64u + +/* Buffers layout options. */ +#define BUF_LOPT_TIMESTAMP 0x1 +#define BUF_LOPT_PARSER_RESULT 0x2 +#define BUF_LOPT_FRAME_STATUS 0x4 +#define BUF_LOPT_PRIV_DATA_SZ 0x8 +#define BUF_LOPT_DATA_ALIGN 0x10 +#define BUF_LOPT_DATA_HEAD_ROOM 0x20 +#define BUF_LOPT_DATA_TAIL_ROOM 0x40 + +#define DPAA2_NI_BUF_ADDR_MASK (0x1FFFFFFFFFFFFul) /* 49-bit addresses max. */ +#define DPAA2_NI_BUF_CHAN_MASK (0xFu) +#define DPAA2_NI_BUF_CHAN_SHIFT (60) +#define DPAA2_NI_BUF_IDX_MASK (0x7FFFu) +#define DPAA2_NI_BUF_IDX_SHIFT (49) +#define DPAA2_NI_TX_IDX_MASK (0x7u) +#define DPAA2_NI_TX_IDX_SHIFT (57) +#define DPAA2_NI_TXBUF_IDX_MASK (0xFFu) +#define DPAA2_NI_TXBUF_IDX_SHIFT (49) + +#define DPAA2_NI_FD_FMT_MASK (0x3u) +#define DPAA2_NI_FD_FMT_SHIFT (12) +#define DPAA2_NI_FD_ERR_MASK (0xFFu) +#define DPAA2_NI_FD_ERR_SHIFT (0) +#define DPAA2_NI_FD_SL_MASK (0x1u) +#define DPAA2_NI_FD_SL_SHIFT (14) +#define DPAA2_NI_FD_LEN_MASK (0x3FFFFu) +#define DPAA2_NI_FD_OFFSET_MASK (0x0FFFu) + +/* Enables TCAM for Flow Steering and QoS look-ups. */ +#define DPNI_OPT_HAS_KEY_MASKING 0x10 + +/* Unique IDs for the supported Rx classification header fields. */ +#define DPAA2_ETH_DIST_ETHDST BIT(0) +#define DPAA2_ETH_DIST_ETHSRC BIT(1) +#define DPAA2_ETH_DIST_ETHTYPE BIT(2) +#define DPAA2_ETH_DIST_VLAN BIT(3) +#define DPAA2_ETH_DIST_IPSRC BIT(4) +#define DPAA2_ETH_DIST_IPDST BIT(5) +#define DPAA2_ETH_DIST_IPPROTO BIT(6) +#define DPAA2_ETH_DIST_L4SRC BIT(7) +#define DPAA2_ETH_DIST_L4DST BIT(8) +#define DPAA2_ETH_DIST_ALL (~0ULL) + +/* L3-L4 network traffic flow hash options. */ +#define RXH_L2DA (1 << 1) +#define RXH_VLAN (1 << 2) +#define RXH_L3_PROTO (1 << 3) +#define RXH_IP_SRC (1 << 4) +#define RXH_IP_DST (1 << 5) +#define RXH_L4_B_0_1 (1 << 6) /* src port in case of TCP/UDP/SCTP */ +#define RXH_L4_B_2_3 (1 << 7) /* dst port in case of TCP/UDP/SCTP */ +#define RXH_DISCARD (1 << 31) + +/* Default Rx hash options, set during attaching. */ +#define DPAA2_RXH_DEFAULT (RXH_IP_SRC | RXH_IP_DST | RXH_L4_B_0_1 | RXH_L4_B_2_3) + +MALLOC_DEFINE(M_DPAA2_NI, "dpaa2_ni", "DPAA2 Network Interface"); + +/* DPAA2 Network Interface resource specification. */ +struct resource_spec dpaa2_ni_spec[] = { + /* + * DPMCP resources. + * + * NOTE: MC command portals (MCPs) are used to send commands to, and + * receive responses from, the MC firmware. One portal per DPNI. + */ +#define MCP_RES_NUM (1u) +#define MCP_RID_OFF (0u) +#define MCP_RID(rid) ((rid) + MCP_RID_OFF) + /* --- */ + { DPAA2_DEV_MCP, MCP_RID(0), RF_ACTIVE | RF_SHAREABLE | RF_OPTIONAL }, + /* + * DPIO resources (software portals). + * + * NOTE: One per running core. While DPIOs are the source of data + * availability interrupts, the DPCONs are used to identify the + * network interface that has produced ingress data to that core. + */ +#define IO_RES_NUM (16u) +#define IO_RID_OFF (MCP_RID_OFF + MCP_RES_NUM) +#define IO_RID(rid) ((rid) + IO_RID_OFF) + /* --- */ + { DPAA2_DEV_IO, IO_RID(0), RF_ACTIVE | RF_SHAREABLE }, + { DPAA2_DEV_IO, IO_RID(1), RF_ACTIVE | RF_SHAREABLE | RF_OPTIONAL }, + { DPAA2_DEV_IO, IO_RID(2), RF_ACTIVE | RF_SHAREABLE | RF_OPTIONAL }, + { DPAA2_DEV_IO, IO_RID(3), RF_ACTIVE | RF_SHAREABLE | RF_OPTIONAL }, + { DPAA2_DEV_IO, IO_RID(4), RF_ACTIVE | RF_SHAREABLE | RF_OPTIONAL }, + { DPAA2_DEV_IO, IO_RID(5), RF_ACTIVE | RF_SHAREABLE | RF_OPTIONAL }, + { DPAA2_DEV_IO, IO_RID(6), RF_ACTIVE | RF_SHAREABLE | RF_OPTIONAL }, + { DPAA2_DEV_IO, IO_RID(7), RF_ACTIVE | RF_SHAREABLE | RF_OPTIONAL }, + { DPAA2_DEV_IO, IO_RID(8), RF_ACTIVE | RF_SHAREABLE | RF_OPTIONAL }, + { DPAA2_DEV_IO, IO_RID(9), RF_ACTIVE | RF_SHAREABLE | RF_OPTIONAL }, + { DPAA2_DEV_IO, IO_RID(10), RF_ACTIVE | RF_SHAREABLE | RF_OPTIONAL }, + { DPAA2_DEV_IO, IO_RID(11), RF_ACTIVE | RF_SHAREABLE | RF_OPTIONAL }, + { DPAA2_DEV_IO, IO_RID(12), RF_ACTIVE | RF_SHAREABLE | RF_OPTIONAL }, + { DPAA2_DEV_IO, IO_RID(13), RF_ACTIVE | RF_SHAREABLE | RF_OPTIONAL }, + { DPAA2_DEV_IO, IO_RID(14), RF_ACTIVE | RF_SHAREABLE | RF_OPTIONAL }, + { DPAA2_DEV_IO, IO_RID(15), RF_ACTIVE | RF_SHAREABLE | RF_OPTIONAL }, + /* + * DPBP resources (buffer pools). + * + * NOTE: One per network interface. + */ +#define BP_RES_NUM (1u) +#define BP_RID_OFF (IO_RID_OFF + IO_RES_NUM) +#define BP_RID(rid) ((rid) + BP_RID_OFF) + /* --- */ + { DPAA2_DEV_BP, BP_RID(0), RF_ACTIVE }, + /* + * DPCON resources (channels). + * + * NOTE: One DPCON per core where Rx or Tx confirmation traffic to be + * distributed to. + * NOTE: Since it is necessary to distinguish between traffic from + * different network interfaces arriving on the same core, the + * DPCONs must be private to the DPNIs. + */ +#define CON_RES_NUM (16u) +#define CON_RID_OFF (BP_RID_OFF + BP_RES_NUM) +#define CON_RID(rid) ((rid) + CON_RID_OFF) + /* --- */ + { DPAA2_DEV_CON, CON_RID(0), RF_ACTIVE }, + { DPAA2_DEV_CON, CON_RID(1), RF_ACTIVE | RF_OPTIONAL }, + { DPAA2_DEV_CON, CON_RID(2), RF_ACTIVE | RF_OPTIONAL }, + { DPAA2_DEV_CON, CON_RID(3), RF_ACTIVE | RF_OPTIONAL }, + { DPAA2_DEV_CON, CON_RID(4), RF_ACTIVE | RF_OPTIONAL }, + { DPAA2_DEV_CON, CON_RID(5), RF_ACTIVE | RF_OPTIONAL }, + { DPAA2_DEV_CON, CON_RID(6), RF_ACTIVE | RF_OPTIONAL }, + { DPAA2_DEV_CON, CON_RID(7), RF_ACTIVE | RF_OPTIONAL }, + { DPAA2_DEV_CON, CON_RID(8), RF_ACTIVE | RF_OPTIONAL }, + { DPAA2_DEV_CON, CON_RID(9), RF_ACTIVE | RF_OPTIONAL }, + { DPAA2_DEV_CON, CON_RID(10), RF_ACTIVE | RF_OPTIONAL }, + { DPAA2_DEV_CON, CON_RID(11), RF_ACTIVE | RF_OPTIONAL }, + { DPAA2_DEV_CON, CON_RID(12), RF_ACTIVE | RF_OPTIONAL }, + { DPAA2_DEV_CON, CON_RID(13), RF_ACTIVE | RF_OPTIONAL }, + { DPAA2_DEV_CON, CON_RID(14), RF_ACTIVE | RF_OPTIONAL }, + { DPAA2_DEV_CON, CON_RID(15), RF_ACTIVE | RF_OPTIONAL }, + /* --- */ + RESOURCE_SPEC_END +}; + +/* Supported header fields for Rx hash distribution key */ +static const struct dpaa2_eth_dist_fields dist_fields[] = { + { + /* L2 header */ + .rxnfc_field = RXH_L2DA, + .cls_prot = NET_PROT_ETH, + .cls_field = NH_FLD_ETH_DA, + .id = DPAA2_ETH_DIST_ETHDST, + .size = 6, + }, { + .cls_prot = NET_PROT_ETH, + .cls_field = NH_FLD_ETH_SA, + .id = DPAA2_ETH_DIST_ETHSRC, + .size = 6, + }, { + /* This is the last ethertype field parsed: + * depending on frame format, it can be the MAC ethertype + * or the VLAN etype. + */ + .cls_prot = NET_PROT_ETH, + .cls_field = NH_FLD_ETH_TYPE, + .id = DPAA2_ETH_DIST_ETHTYPE, + .size = 2, + }, { + /* VLAN header */ + .rxnfc_field = RXH_VLAN, + .cls_prot = NET_PROT_VLAN, + .cls_field = NH_FLD_VLAN_TCI, + .id = DPAA2_ETH_DIST_VLAN, + .size = 2, + }, { + /* IP header */ + .rxnfc_field = RXH_IP_SRC, + .cls_prot = NET_PROT_IP, + .cls_field = NH_FLD_IP_SRC, + .id = DPAA2_ETH_DIST_IPSRC, + .size = 4, + }, { + .rxnfc_field = RXH_IP_DST, + .cls_prot = NET_PROT_IP, + .cls_field = NH_FLD_IP_DST, + .id = DPAA2_ETH_DIST_IPDST, + .size = 4, + }, { + .rxnfc_field = RXH_L3_PROTO, + .cls_prot = NET_PROT_IP, + .cls_field = NH_FLD_IP_PROTO, + .id = DPAA2_ETH_DIST_IPPROTO, + .size = 1, + }, { + /* Using UDP ports, this is functionally equivalent to raw + * byte pairs from L4 header. + */ + .rxnfc_field = RXH_L4_B_0_1, + .cls_prot = NET_PROT_UDP, + .cls_field = NH_FLD_UDP_PORT_SRC, + .id = DPAA2_ETH_DIST_L4SRC, + .size = 2, + }, { + .rxnfc_field = RXH_L4_B_2_3, + .cls_prot = NET_PROT_UDP, + .cls_field = NH_FLD_UDP_PORT_DST, + .id = DPAA2_ETH_DIST_L4DST, + .size = 2, + }, +}; + +static struct dpni_stat { + int page; + int cnt; + char *name; + char *desc; +} dpni_stat_sysctls[DPAA2_NI_STAT_SYSCTLS] = { + /* PAGE, COUNTER, NAME, DESCRIPTION */ + { 0, 0, "in_all_frames", "All accepted ingress frames" }, + { 0, 1, "in_all_bytes", "Bytes in all accepted ingress frames" }, + { 0, 2, "in_multi_frames", "Multicast accepted ingress frames" }, + { 1, 0, "eg_all_frames", "All egress frames transmitted" }, + { 1, 1, "eg_all_bytes", "Bytes in all frames transmitted" }, + { 1, 2, "eg_multi_frames", "Multicast egress frames transmitted" }, + { 2, 0, "in_filtered_frames", "All ingress frames discarded due to " + "filtering" }, + { 2, 1, "in_discarded_frames", "All frames discarded due to errors" }, + { 2, 2, "in_nobuf_discards", "Discards on ingress side due to buffer " + "depletion in DPNI buffer pools" }, +}; + +/* Device interface */ +static int dpaa2_ni_probe(device_t); +static int dpaa2_ni_attach(device_t); +static int dpaa2_ni_detach(device_t); + +/* DPAA2 network interface setup and configuration */ +static int dpaa2_ni_setup(device_t); +static int dpaa2_ni_setup_channels(device_t); +static int dpaa2_ni_setup_fq(device_t, struct dpaa2_ni_channel *, + enum dpaa2_ni_queue_type); +static int dpaa2_ni_bind(device_t); +static int dpaa2_ni_setup_rx_dist(device_t); +static int dpaa2_ni_setup_irqs(device_t); +static int dpaa2_ni_setup_msi(struct dpaa2_ni_softc *); +static int dpaa2_ni_setup_if_caps(struct dpaa2_ni_softc *); +static int dpaa2_ni_setup_if_flags(struct dpaa2_ni_softc *); +static int dpaa2_ni_setup_sysctls(struct dpaa2_ni_softc *); +static int dpaa2_ni_setup_dma(struct dpaa2_ni_softc *); + +/* Tx/Rx flow configuration */ +static int dpaa2_ni_setup_rx_flow(device_t, struct dpaa2_cmd *, + struct dpaa2_ni_fq *); +static int dpaa2_ni_setup_tx_flow(device_t, struct dpaa2_cmd *, + struct dpaa2_ni_fq *); +static int dpaa2_ni_setup_rx_err_flow(device_t, struct dpaa2_cmd *, + struct dpaa2_ni_fq *); + +/* Configuration subroutines */ +static int dpaa2_ni_set_buf_layout(device_t, struct dpaa2_cmd *); +static int dpaa2_ni_set_pause_frame(device_t, struct dpaa2_cmd *); +static int dpaa2_ni_set_qos_table(device_t, struct dpaa2_cmd *); +static int dpaa2_ni_set_mac_addr(device_t, struct dpaa2_cmd *, uint16_t, + uint16_t); +static int dpaa2_ni_set_hash(device_t, uint64_t); +static int dpaa2_ni_set_dist_key(device_t, enum dpaa2_ni_dist_mode, uint64_t); + +/* Buffers and buffer pools */ +static int dpaa2_ni_seed_buf_pool(struct dpaa2_ni_softc *, uint32_t); +static int dpaa2_ni_seed_rxbuf(struct dpaa2_ni_softc *, struct dpaa2_buf *, int); +static int dpaa2_ni_seed_txbuf(struct dpaa2_ni_softc *, struct dpaa2_buf *, int); +static int dpaa2_ni_seed_chan_storage(struct dpaa2_ni_softc *, + struct dpaa2_ni_channel *); + +/* Frame descriptor routines */ +static int dpaa2_ni_build_fd(struct dpaa2_ni_softc *, struct dpaa2_ni_tx_ring *, + struct dpaa2_buf *, bus_dma_segment_t *, int, struct dpaa2_fd *); +static int dpaa2_ni_fd_err(struct dpaa2_fd *); +static uint32_t dpaa2_ni_fd_data_len(struct dpaa2_fd *); +static int dpaa2_ni_fd_chan_idx(struct dpaa2_fd *); +static int dpaa2_ni_fd_buf_idx(struct dpaa2_fd *); +static int dpaa2_ni_fd_tx_idx(struct dpaa2_fd *); +static int dpaa2_ni_fd_txbuf_idx(struct dpaa2_fd *); +static int dpaa2_ni_fd_format(struct dpaa2_fd *); +static bool dpaa2_ni_fd_short_len(struct dpaa2_fd *); +static int dpaa2_ni_fd_offset(struct dpaa2_fd *); + +/* Various subroutines */ +static int dpaa2_ni_cmp_api_version(struct dpaa2_ni_softc *, uint16_t, uint16_t); +static int dpaa2_ni_prepare_key_cfg(struct dpkg_profile_cfg *, uint8_t *); +static int dpaa2_ni_chan_storage_next(struct dpaa2_ni_channel *, + struct dpaa2_dq **); + +/* Network interface routines */ +static void dpaa2_ni_init(void *); +static int dpaa2_ni_transmit(struct ifnet *, struct mbuf *); +static void dpaa2_ni_qflush(struct ifnet *); +static int dpaa2_ni_ioctl(struct ifnet *, u_long, caddr_t); +static int dpaa2_ni_update_mac_filters(struct ifnet *); +static u_int dpaa2_ni_add_maddr(void *, struct sockaddr_dl *, u_int); + +/* Interrupt handlers */ +static void dpaa2_ni_intr(void *); + +/* MII handlers */ +static void dpaa2_ni_miibus_statchg(device_t); +static int dpaa2_ni_media_change(struct ifnet *); +static void dpaa2_ni_media_status(struct ifnet *, struct ifmediareq *); +static void dpaa2_ni_media_tick(void *); + +/* DMA mapping callback */ +static void dpaa2_ni_dmamap_cb(void *, bus_dma_segment_t *, int, int); + +/* Tx/Rx routines. */ +static void dpaa2_ni_poll(void *); +static void dpaa2_ni_tx_locked(struct dpaa2_ni_softc *, + struct dpaa2_ni_tx_ring *, struct mbuf *); +static void dpaa2_ni_bp_task(void *, int); + +/* Tx/Rx subroutines */ +static int dpaa2_ni_consume_frames(struct dpaa2_ni_channel *, + struct dpaa2_ni_fq **, uint32_t *); +static int dpaa2_ni_rx(struct dpaa2_ni_channel *, struct dpaa2_ni_fq *, + struct dpaa2_fd *); +static int dpaa2_ni_rx_err(struct dpaa2_ni_channel *, struct dpaa2_ni_fq *, + struct dpaa2_fd *); +static int dpaa2_ni_tx_conf(struct dpaa2_ni_channel *, struct dpaa2_ni_fq *, + struct dpaa2_fd *); + +/* sysctl(9) */ +static int dpaa2_ni_collect_stats(SYSCTL_HANDLER_ARGS); +static int dpaa2_ni_collect_buf_num(SYSCTL_HANDLER_ARGS); +static int dpaa2_ni_collect_buf_free(SYSCTL_HANDLER_ARGS); + +static int +dpaa2_ni_probe(device_t dev) +{ + /* DPNI device will be added by a parent resource container itself. */ + device_set_desc(dev, "DPAA2 Network Interface"); + return (BUS_PROBE_DEFAULT); +} + +static int +dpaa2_ni_attach(device_t dev) +{ + device_t pdev = device_get_parent(dev); + device_t child = dev; + device_t mcp_dev; + struct dpaa2_ni_softc *sc = device_get_softc(dev); + struct dpaa2_devinfo *rcinfo = device_get_ivars(pdev); + struct dpaa2_devinfo *dinfo = device_get_ivars(dev); + struct dpaa2_devinfo *mcp_dinfo; + struct ifnet *ifp; + char tq_name[32]; + int error; + + sc->dev = dev; + sc->ifp = NULL; + sc->miibus = NULL; + sc->mii = NULL; + sc->media_status = 0; + sc->if_flags = 0; + sc->link_state = LINK_STATE_UNKNOWN; + sc->buf_align = 0; + + /* For debug purposes only! */ + sc->rx_anomaly_frames = 0; + sc->rx_single_buf_frames = 0; + sc->rx_sg_buf_frames = 0; + sc->rx_enq_rej_frames = 0; + sc->rx_ieoi_err_frames = 0; + sc->tx_single_buf_frames = 0; + sc->tx_sg_frames = 0; + + DPAA2_ATOMIC_XCHG(&sc->buf_num, 0); + DPAA2_ATOMIC_XCHG(&sc->buf_free, 0); + + sc->bp_dmat = NULL; + sc->st_dmat = NULL; + sc->rxd_dmat = NULL; + sc->qos_dmat = NULL; + + sc->qos_kcfg.type = DPAA2_BUF_STORE; + sc->qos_kcfg.store.dmap = NULL; + sc->qos_kcfg.store.paddr = 0; + sc->qos_kcfg.store.vaddr = NULL; + + sc->rxd_kcfg.type = DPAA2_BUF_STORE; + sc->rxd_kcfg.store.dmap = NULL; + sc->rxd_kcfg.store.paddr = 0; + sc->rxd_kcfg.store.vaddr = NULL; + + sc->mac.dpmac_id = 0; + sc->mac.phy_dev = NULL; + memset(sc->mac.addr, 0, ETHER_ADDR_LEN); + + error = bus_alloc_resources(sc->dev, dpaa2_ni_spec, sc->res); + if (error) { + device_printf(dev, "%s: failed to allocate resources: " + "error=%d\n", __func__, error); + return (ENXIO); + } + + /* Obtain MC portal. */ + mcp_dev = (device_t) rman_get_start(sc->res[MCP_RID(0)]); + mcp_dinfo = device_get_ivars(mcp_dev); + dinfo->portal = mcp_dinfo->portal; + + mtx_init(&sc->lock, device_get_nameunit(dev), "dpaa2_ni", MTX_DEF); + + /* Allocate network interface */ + ifp = if_alloc(IFT_ETHER); + if (ifp == NULL) { + device_printf(dev, "%s: failed to allocate network interface\n", + __func__); + return (ENXIO); + } + sc->ifp = ifp; + if_initname(ifp, DPAA2_NI_IFNAME, device_get_unit(sc->dev)); + + ifp->if_softc = sc; + ifp->if_flags = IFF_SIMPLEX | IFF_MULTICAST | IFF_BROADCAST; + ifp->if_init = dpaa2_ni_init; + ifp->if_ioctl = dpaa2_ni_ioctl; + ifp->if_transmit = dpaa2_ni_transmit; + ifp->if_qflush = dpaa2_ni_qflush; + + ifp->if_capabilities = IFCAP_VLAN_MTU | IFCAP_HWCSUM | IFCAP_JUMBO_MTU; + ifp->if_capenable = ifp->if_capabilities; + + /* Allocate a command to send to MC hardware. */ + error = dpaa2_mcp_init_command(&sc->cmd, DPAA2_CMD_DEF); + if (error) { + device_printf(dev, "%s: failed to allocate dpaa2_cmd: " + "error=%d\n", __func__, error); + goto err_exit; + } + + /* Open resource container and network interface object. */ + error = DPAA2_CMD_RC_OPEN(dev, child, sc->cmd, rcinfo->id, + &sc->rc_token); + if (error) { + device_printf(dev, "%s: failed to open resource container: " + "id=%d, error=%d\n", __func__, rcinfo->id, error); + goto err_free_cmd; + } + error = DPAA2_CMD_NI_OPEN(dev, child, dpaa2_mcp_tk(sc->cmd, + sc->rc_token), dinfo->id, &sc->ni_token); + if (error) { + device_printf(dev, "%s: failed to open network interface: " + "id=%d, error=%d\n", __func__, dinfo->id, error); + goto err_close_rc; + } + + /* Create a taskqueue thread to release new buffers to the pool. */ + TASK_INIT(&sc->bp_task, 0, dpaa2_ni_bp_task, sc); + bzero(tq_name, sizeof (tq_name)); + snprintf(tq_name, sizeof (tq_name), "%s_tqbp", + device_get_nameunit(dev)); + sc->bp_taskq = taskqueue_create(tq_name, M_WAITOK, + taskqueue_thread_enqueue, &sc->bp_taskq); + if (sc->bp_taskq == NULL) { + device_printf(dev, "%s: failed to allocate task queue: %s\n", + __func__, tq_name); + goto err_close_ni; + } + taskqueue_start_threads(&sc->bp_taskq, 1, PI_NET, "%s", tq_name); + + error = dpaa2_ni_setup(dev); + if (error) { + device_printf(dev, "%s: failed to setup DPNI: error=%d\n", + __func__, error); + goto err_close_ni; + } + error = dpaa2_ni_setup_channels(dev); + if (error) { + device_printf(dev, "%s: failed to setup QBMan channels: " + "error=%d\n", __func__, error); + goto err_close_ni; + } + + error = dpaa2_ni_bind(dev); + if (error) { + device_printf(dev, "%s: failed to bind DPNI: error=%d\n", + __func__, error); + goto err_close_ni; + } + error = dpaa2_ni_setup_irqs(dev); + if (error) { + device_printf(dev, "%s: failed to setup IRQs: error=%d\n", + __func__, error); + goto err_close_ni; + } + error = dpaa2_ni_setup_sysctls(sc); + if (error) { + device_printf(dev, "%s: failed to setup sysctls: error=%d\n", + __func__, error); + goto err_close_ni; + } + + ether_ifattach(sc->ifp, sc->mac.addr); + callout_init(&sc->mii_callout, 0); + + return (0); + +err_close_ni: + DPAA2_CMD_NI_CLOSE(dev, child, dpaa2_mcp_tk(sc->cmd, sc->ni_token)); +err_close_rc: + DPAA2_CMD_RC_CLOSE(dev, child, dpaa2_mcp_tk(sc->cmd, sc->rc_token)); +err_free_cmd: + dpaa2_mcp_free_command(sc->cmd); +err_exit: + return (ENXIO); +} + +static void +dpaa2_ni_fixed_media_status(if_t ifp, struct ifmediareq* ifmr) +{ + struct dpaa2_ni_softc *sc = ifp->if_softc; + + DPNI_LOCK(sc); + ifmr->ifm_count = 0; + ifmr->ifm_mask = 0; + ifmr->ifm_status = IFM_AVALID | IFM_ACTIVE; + ifmr->ifm_current = ifmr->ifm_active = + sc->fixed_ifmedia.ifm_cur->ifm_media; + + /* + * In non-PHY usecases, we need to signal link state up, otherwise + * certain things requiring a link event (e.g async DHCP client) from + * devd do not happen. + */ + if (ifp->if_link_state == LINK_STATE_UNKNOWN) { + if_link_state_change(ifp, LINK_STATE_UP); + } + + /* + * TODO: Check the status of the link partner (DPMAC, DPNI or other) and + * reset if down. This is different to the DPAA2_MAC_LINK_TYPE_PHY as + * the MC firmware sets the status, instead of us telling the MC what + * it is. + */ + DPNI_UNLOCK(sc); + + return; +} + +static void +dpaa2_ni_setup_fixed_link(struct dpaa2_ni_softc *sc) +{ + /* + * FIXME: When the DPNI is connected to a DPMAC, we can get the + * 'apparent' speed from it. + */ + sc->fixed_link = true; + + ifmedia_init(&sc->fixed_ifmedia, 0, dpaa2_ni_media_change, + dpaa2_ni_fixed_media_status); + ifmedia_add(&sc->fixed_ifmedia, IFM_ETHER | IFM_1000_T, 0, NULL); + ifmedia_set(&sc->fixed_ifmedia, IFM_ETHER | IFM_1000_T); +} + +static int +dpaa2_ni_detach(device_t dev) +{ + device_t child = dev; + struct dpaa2_ni_softc *sc = device_get_softc(dev); + + DPAA2_CMD_NI_CLOSE(dev, child, dpaa2_mcp_tk(sc->cmd, sc->ni_token)); + DPAA2_CMD_RC_CLOSE(dev, child, dpaa2_mcp_tk(sc->cmd, sc->rc_token)); + dpaa2_mcp_free_command(sc->cmd); + + sc->cmd = NULL; + sc->ni_token = 0; + sc->rc_token = 0; + + return (0); +} + +/** + * @brief Configure DPAA2 network interface object. + */ +static int +dpaa2_ni_setup(device_t dev) +{ + device_t child = dev; + struct dpaa2_ni_softc *sc = device_get_softc(dev); + struct dpaa2_devinfo *dinfo = device_get_ivars(dev); + struct dpaa2_ep_desc ep1_desc, ep2_desc; /* endpoint descriptors */ + struct dpaa2_cmd *cmd = sc->cmd; + uint8_t eth_bca[ETHER_ADDR_LEN]; /* broadcast physical address */ + uint16_t rc_token = sc->rc_token; + uint16_t ni_token = sc->ni_token; + uint16_t mac_token; + struct dpaa2_mac_attr attr; + enum dpaa2_mac_link_type link_type; + uint32_t link; + int error; + + /* Check if we can work with this DPNI object. */ + error = DPAA2_CMD_NI_GET_API_VERSION(dev, child, dpaa2_mcp_tk(cmd, + ni_token), &sc->api_major, &sc->api_minor); + if (error) { + device_printf(dev, "%s: failed to get DPNI API version\n", + __func__); + return (error); + } + if (dpaa2_ni_cmp_api_version(sc, DPNI_VER_MAJOR, DPNI_VER_MINOR) < 0) { + device_printf(dev, "%s: DPNI API version %u.%u not supported, " + "need >= %u.%u\n", __func__, sc->api_major, sc->api_minor, + DPNI_VER_MAJOR, DPNI_VER_MINOR); + error = ENODEV; + return (error); + } + + /* Reset the DPNI object. */ + error = DPAA2_CMD_NI_RESET(dev, child, cmd); + if (error) { + device_printf(dev, "%s: failed to reset DPNI: id=%d\n", + __func__, dinfo->id); + return (error); + } + + /* Obtain attributes of the DPNI object. */ + error = DPAA2_CMD_NI_GET_ATTRIBUTES(dev, child, cmd, &sc->attr); + if (error) { + device_printf(dev, "%s: failed to obtain DPNI attributes: " + "id=%d\n", __func__, dinfo->id); + return (error); + } + if (bootverbose) { + device_printf(dev, "options=0x%#x queues=%d tx_channels=%d " + "wriop_version=%#x\n", sc->attr.options, sc->attr.num.queues, + sc->attr.num.channels, sc->attr.wriop_ver); + device_printf(dev, "\ttraffic classes: rx=%d tx=%d " + "cgs_groups=%d\n", sc->attr.num.rx_tcs, sc->attr.num.tx_tcs, + sc->attr.num.cgs); + device_printf(dev, "\ttable entries: mac=%d vlan=%d qos=%d " + "fs=%d\n", sc->attr.entries.mac, sc->attr.entries.vlan, + sc->attr.entries.qos, sc->attr.entries.fs); + device_printf(dev, "\tkey sizes: qos=%d fs=%d\n", + sc->attr.key_size.qos, sc->attr.key_size.fs); + } + + /* Configure buffer layouts of the DPNI queues. */ + error = dpaa2_ni_set_buf_layout(dev, cmd); + if (error) { + device_printf(dev, "%s: failed to configure buffer layout\n", + __func__); + return (error); + } + + /* Configure DMA resources. */ + error = dpaa2_ni_setup_dma(sc); + if (error) { + device_printf(dev, "%s: failed to setup DMA\n", __func__); + return (error); + } + + /* Setup link between DPNI and an object it's connected to. */ + ep1_desc.obj_id = dinfo->id; + ep1_desc.if_id = 0; /* DPNI has the only endpoint */ + ep1_desc.type = dinfo->dtype; + + error = DPAA2_CMD_RC_GET_CONN(dev, child, dpaa2_mcp_tk(cmd, rc_token), + &ep1_desc, &ep2_desc, &link); + if (error) + device_printf(dev, "%s: failed to obtain an object DPNI is " + "connected to: error=%d\n", __func__, error); + else { + device_printf(dev, "connected to %s (id=%d)\n", + dpaa2_ttos(ep2_desc.type), ep2_desc.obj_id); + + error = dpaa2_ni_set_mac_addr(dev, cmd, rc_token, ni_token); + if (error) + device_printf(dev, "%s: failed to set MAC " + "address: error=%d\n", __func__, error); + + if (ep2_desc.type == DPAA2_DEV_MAC) { + /* + * This is the simplest case when DPNI is connected to + * DPMAC directly. + */ + sc->mac.dpmac_id = ep2_desc.obj_id; + + link_type = DPAA2_MAC_LINK_TYPE_NONE; + + /* + * Need to determine if DPMAC type is PHY (attached to + * conventional MII PHY) or FIXED (usually SFP/SerDes, + * link state managed by MC firmware). + */ + error = DPAA2_CMD_MAC_OPEN(sc->dev, child, + dpaa2_mcp_tk(sc->cmd, sc->rc_token), + sc->mac.dpmac_id, &mac_token); + /* + * Under VFIO, the DPMAC might be sitting in another + * container (DPRC) we don't have access to. + * Assume DPAA2_MAC_LINK_TYPE_FIXED if this is + * the case. + */ + if (error) { + device_printf(dev, "%s: failed to open " + "connected DPMAC: %d (assuming in other DPRC)\n", __func__, + sc->mac.dpmac_id); + link_type = DPAA2_MAC_LINK_TYPE_FIXED; + } else { + error = DPAA2_CMD_MAC_GET_ATTRIBUTES(dev, child, + sc->cmd, &attr); + if (error) + device_printf(dev, "%s: failed to get " + "DPMAC attributes: id=%d, " + "error=%d\n", __func__, dinfo->id, + error); + else + link_type = attr.link_type; + } + DPAA2_CMD_MAC_CLOSE(dev, child, dpaa2_mcp_tk(sc->cmd, + mac_token)); + + if (link_type == DPAA2_MAC_LINK_TYPE_FIXED) { + device_printf(dev, "connected DPMAC is in FIXED " + "mode\n"); + dpaa2_ni_setup_fixed_link(sc); + } else if (link_type == DPAA2_MAC_LINK_TYPE_PHY) { + device_printf(dev, "connected DPMAC is in PHY " + "mode\n"); + error = DPAA2_MC_GET_PHY_DEV(dev, + &sc->mac.phy_dev, sc->mac.dpmac_id); + if (error == 0) { + error = MEMAC_MDIO_SET_NI_DEV( + sc->mac.phy_dev, dev); + if (error != 0) + device_printf(dev, "%s: failed " + "to set dpni dev on memac " + "mdio dev %s: error=%d\n", + __func__, + device_get_nameunit( + sc->mac.phy_dev), error); + } + if (error == 0) { + error = MEMAC_MDIO_GET_PHY_LOC( + sc->mac.phy_dev, &sc->mac.phy_loc); + if (error == ENODEV) + error = 0; + if (error != 0) + device_printf(dev, "%s: failed " + "to get phy location from " + "memac mdio dev %s: error=%d\n", + __func__, device_get_nameunit( + sc->mac.phy_dev), error); + } + if (error == 0) { + error = mii_attach(sc->mac.phy_dev, + &sc->miibus, sc->ifp, + dpaa2_ni_media_change, + dpaa2_ni_media_status, + BMSR_DEFCAPMASK, sc->mac.phy_loc, + MII_OFFSET_ANY, 0); + if (error != 0) + device_printf(dev, "%s: failed " + "to attach to miibus: " + "error=%d\n", + __func__, error); + } + if (error == 0) + sc->mii = device_get_softc(sc->miibus); + } else { + device_printf(dev, "%s: DPMAC link type is not " + "supported\n", __func__); + } + } else if (ep2_desc.type == DPAA2_DEV_NI || + ep2_desc.type == DPAA2_DEV_MUX || + ep2_desc.type == DPAA2_DEV_SW) { + dpaa2_ni_setup_fixed_link(sc); + } + } + + /* Select mode to enqueue frames. */ + /* ... TBD ... */ + + /* + * Update link configuration to enable Rx/Tx pause frames support. + * + * NOTE: MC may generate an interrupt to the DPMAC and request changes + * in link configuration. It might be necessary to attach miibus + * and PHY before this point. + */ + error = dpaa2_ni_set_pause_frame(dev, dpaa2_mcp_tk(cmd, ni_token)); + if (error) { + device_printf(dev, "%s: failed to configure Rx/Tx pause " + "frames\n", __func__); + return (error); + } + + /* Configure ingress traffic classification. */ + error = dpaa2_ni_set_qos_table(dev, dpaa2_mcp_tk(cmd, ni_token)); + if (error) + device_printf(dev, "%s: failed to configure QoS table: " + "error=%d\n", __func__, error); + + /* Add broadcast physical address to the MAC filtering table. */ + memset(eth_bca, 0xff, ETHER_ADDR_LEN); + error = DPAA2_CMD_NI_ADD_MAC_ADDR(dev, child, cmd, eth_bca); + if (error) { + device_printf(dev, "%s: failed to add broadcast physical " + "address to the MAC filtering table\n", __func__); + return (error); + } + + /* Set the maximum allowed length for received frames. */ + error = DPAA2_CMD_NI_SET_MFL(dev, child, cmd, DPAA2_ETH_MFL); + if (error) { + device_printf(dev, "%s: failed to set maximum length for " + "received frames\n", __func__); + return (error); + } + + return (0); +} + +/** + * @brief Сonfigure QBMan channels and register data availability notifications. + */ +static int +dpaa2_ni_setup_channels(device_t dev) +{ + struct dpaa2_ni_softc *sc = device_get_softc(dev); + struct dpaa2_con_softc *consc; + struct dpaa2_devinfo *io_info, *con_info; + device_t io_dev, con_dev, child = dev; + struct dpaa2_ni_channel *channel; + struct dpaa2_io_notif_ctx *ctx; + struct dpaa2_con_notif_cfg notif_cfg; + struct dpaa2_buf *buf; + int error; + struct sysctl_ctx_list *sysctl_ctx; + struct sysctl_oid *node; + struct sysctl_oid_list *parent; + uint32_t i, num_chan; + + /* Calculate number of the channels based on the allocated resources. */ + for (i = 0; i < IO_RES_NUM; i++) + if (!sc->res[IO_RID(i)]) + break; + num_chan = i; + for (i = 0; i < CON_RES_NUM; i++) + if (!sc->res[CON_RID(i)]) + break; + num_chan = i < num_chan ? i : num_chan; + + /* Limit maximum channels. */ + sc->chan_n = num_chan > DPAA2_NI_MAX_CHANNELS + ? DPAA2_NI_MAX_CHANNELS : num_chan; + + /* Limit channels by number of the queues. */ + sc->chan_n = sc->chan_n > sc->attr.num.queues + ? sc->attr.num.queues : sc->chan_n; + + device_printf(dev, "channels=%d\n", sc->chan_n); + + sysctl_ctx = device_get_sysctl_ctx(sc->dev); + parent = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)); + + node = SYSCTL_ADD_NODE(sysctl_ctx, parent, OID_AUTO, "channels", + CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "DPNI Channels"); + parent = SYSCTL_CHILDREN(node); + + /* Setup channels for the portal. */ + for (uint32_t i = 0; i < sc->chan_n; i++) { + /* Select software portal. */ + io_dev = (device_t) rman_get_start(sc->res[IO_RID(i)]); + io_info = device_get_ivars(io_dev); + + /* Select DPCON (channel). */ + con_dev = (device_t) rman_get_start(sc->res[CON_RID(i)]); + consc = device_get_softc(con_dev); + con_info = device_get_ivars(con_dev); + + /* Enable selected channel. */ + error = DPAA2_CMD_CON_ENABLE(dev, child, dpaa2_mcp_tk(consc->cmd, + consc->con_token)); + if (error) { + device_printf(dev, "%s: failed to enable channel: " + "dpcon_id=%d, chan_id=%d\n", __func__, con_info->id, + consc->attr.chan_id); + return (error); + } + + channel = malloc(sizeof(struct dpaa2_ni_channel), M_DPAA2_NI, + M_WAITOK | M_ZERO); + if (!channel) { + device_printf(dev, "%s: failed to allocate a channel\n", + __func__); + return (ENOMEM); + } + + sc->channels[i] = channel; + + channel->id = consc->attr.chan_id; + channel->flowid = i; + channel->ni_dev = dev; + channel->io_dev = io_dev; + channel->con_dev = con_dev; + channel->recycled_n = 0; + + buf = &channel->store; + buf->type = DPAA2_BUF_STORE; + buf->store.dmat = NULL; + buf->store.dmap = NULL; + buf->store.paddr = 0; + buf->store.vaddr = NULL; + + /* For debug purposes only! */ + channel->tx_frames = 0; + channel->tx_dropped = 0; + + /* None of the frame queues for this channel configured yet. */ + channel->rxq_n = 0; + + /* Setup WQ channel notification context. */ + ctx = &channel->ctx; + ctx->qman_ctx = (uint64_t) ctx; + ctx->cdan_en = true; + ctx->fq_chan_id = channel->id; + ctx->io_dev = channel->io_dev; + ctx->channel = channel; + ctx->poll = dpaa2_ni_poll; + + /* Register the new notification context. */ + error = DPAA2_SWP_CONF_WQ_CHANNEL(channel->io_dev, ctx); + if (error) { + device_printf(dev, "%s: failed to register notification " + "context\n", __func__); + return (error); + } + + /* Register DPCON notification with Management Complex. */ + notif_cfg.dpio_id = io_info->id; + notif_cfg.prior = 0; + notif_cfg.qman_ctx = ctx->qman_ctx; + error = DPAA2_CMD_CON_SET_NOTIF(dev, child, dpaa2_mcp_tk( + consc->cmd, consc->con_token), ¬if_cfg); + if (error) { + device_printf(dev, "%s: failed to set DPCON " + "notification: dpcon_id=%d, chan_id=%d\n", __func__, + con_info->id, consc->attr.chan_id); + return (error); + } + + /* Allocate initial # of Rx buffers and a channel storage. */ + error = dpaa2_ni_seed_buf_pool(sc, DPAA2_NI_BUFS_INIT); + if (error) { + device_printf(dev, "%s: failed to seed buffer pool\n", + __func__); + return (error); + } + error = dpaa2_ni_seed_chan_storage(sc, channel); + if (error) { + device_printf(dev, "%s: failed to seed channel " + "storage\n", __func__); + return (error); + } + + /* Prepare queues for this channel. */ + error = dpaa2_ni_setup_fq(dev, channel, DPAA2_NI_QUEUE_TX_CONF); + if (error) { + device_printf(dev, "%s: failed to prepare TxConf " + "queue: error=%d\n", __func__, error); + return (error); + } + error = dpaa2_ni_setup_fq(dev, channel, DPAA2_NI_QUEUE_RX); + if (error) { + device_printf(dev, "%s: failed to prepare Rx queue: " + "error=%d\n", __func__, error); + return (error); + } + + if (bootverbose) + device_printf(dev, "channel: dpio_id=%d " + "dpcon_id=%d chan_id=%d, priorities=%d\n", + io_info->id, con_info->id, channel->id, + consc->attr.prior_num); + } + + /* There is exactly one Rx error queue per DPNI. */ + error = dpaa2_ni_setup_fq(dev, sc->channels[0], DPAA2_NI_QUEUE_RX_ERR); + if (error) { + device_printf(dev, "%s: failed to prepare RxError queue: " + "error=%d\n", __func__, error); + return (error); + } + + return (0); +} + +/** + * @brief Performs an initial configuration of the frame queues. + */ +static int +dpaa2_ni_setup_fq(device_t dev, struct dpaa2_ni_channel *chan, + enum dpaa2_ni_queue_type queue_type) +{ + struct dpaa2_ni_softc *sc = device_get_softc(dev); + struct dpaa2_ni_fq *fq; + + switch (queue_type) { + case DPAA2_NI_QUEUE_TX_CONF: + /* One queue per channel. */ + fq = &chan->txc_queue; + + fq->consume = dpaa2_ni_tx_conf; + fq->chan = chan; + fq->flowid = chan->flowid; + fq->tc = 0; /* ignored */ + fq->type = queue_type; + + break; + case DPAA2_NI_QUEUE_RX: + KASSERT(sc->attr.num.rx_tcs <= DPAA2_NI_MAX_TCS, + ("too many Rx traffic classes: rx_tcs=%d\n", + sc->attr.num.rx_tcs)); + + /* One queue per Rx traffic class within a channel. */ + for (int i = 0; i < sc->attr.num.rx_tcs; i++) { + fq = &chan->rx_queues[i]; + + fq->consume = dpaa2_ni_rx; + fq->chan = chan; + fq->flowid = chan->flowid; + fq->tc = (uint8_t) i; + fq->type = queue_type; + + chan->rxq_n++; + } + break; + case DPAA2_NI_QUEUE_RX_ERR: + /* One queue per network interface. */ + fq = &sc->rxe_queue; + + fq->consume = dpaa2_ni_rx_err; + fq->chan = chan; + fq->flowid = 0; /* ignored */ + fq->tc = 0; /* ignored */ + fq->type = queue_type; + break; + default: + device_printf(dev, "%s: unexpected frame queue type: %d\n", + __func__, queue_type); + return (EINVAL); + } + + return (0); +} + +/** + * @brief Bind DPNI to DPBPs, DPIOs, frame queues and channels. + */ +static int +dpaa2_ni_bind(device_t dev) +{ + device_t bp_dev, child = dev; + struct dpaa2_ni_softc *sc = device_get_softc(dev); + struct dpaa2_devinfo *bp_info; + struct dpaa2_cmd *cmd = sc->cmd; + struct dpaa2_ni_pools_cfg pools_cfg; + struct dpaa2_ni_err_cfg err_cfg; + struct dpaa2_ni_channel *chan; + uint16_t ni_token = sc->ni_token; + int error; + + /* Select buffer pool (only one available at the moment). */ + bp_dev = (device_t) rman_get_start(sc->res[BP_RID(0)]); + bp_info = device_get_ivars(bp_dev); + + /* Configure buffers pool. */ + pools_cfg.pools_num = 1; + pools_cfg.pools[0].bp_obj_id = bp_info->id; + pools_cfg.pools[0].backup_flag = 0; + pools_cfg.pools[0].buf_sz = sc->buf_sz; + error = DPAA2_CMD_NI_SET_POOLS(dev, child, dpaa2_mcp_tk(cmd, ni_token), + &pools_cfg); + if (error) { + device_printf(dev, "%s: failed to set buffer pools\n", __func__); + return (error); + } + + /* Setup ingress traffic distribution. */ + error = dpaa2_ni_setup_rx_dist(dev); + if (error && error != EOPNOTSUPP) { + device_printf(dev, "%s: failed to setup ingress traffic " + "distribution\n", __func__); + return (error); + } + if (bootverbose && error == EOPNOTSUPP) + device_printf(dev, "Ingress traffic distribution not " + "supported\n"); + + /* Configure handling of error frames. */ + err_cfg.err_mask = DPAA2_NI_FAS_RX_ERR_MASK; + err_cfg.set_err_fas = false; + err_cfg.action = DPAA2_NI_ERR_DISCARD; + error = DPAA2_CMD_NI_SET_ERR_BEHAVIOR(dev, child, cmd, &err_cfg); + if (error) { + device_printf(dev, "%s: failed to set errors behavior\n", + __func__); + return (error); + } + + /* Configure channel queues to generate CDANs. */ + for (uint32_t i = 0; i < sc->chan_n; i++) { + chan = sc->channels[i]; + + /* Setup Rx flows. */ + for (uint32_t j = 0; j < chan->rxq_n; j++) { + error = dpaa2_ni_setup_rx_flow(dev, cmd, + &chan->rx_queues[j]); + if (error) { + device_printf(dev, "%s: failed to setup Rx " + "flow: error=%d\n", __func__, error); + return (error); + } + } + + /* Setup Tx flow. */ + error = dpaa2_ni_setup_tx_flow(dev, cmd, &chan->txc_queue); + if (error) { + device_printf(dev, "%s: failed to setup Tx " + "flow: error=%d\n", __func__, error); + return (error); + } + } + + /* Configure RxError queue to generate CDAN. */ + error = dpaa2_ni_setup_rx_err_flow(dev, cmd, &sc->rxe_queue); + if (error) { + device_printf(dev, "%s: failed to setup RxError flow: " + "error=%d\n", __func__, error); + return (error); + } + + /* + * Get the Queuing Destination ID (QDID) that should be used for frame + * enqueue operations. + */ + error = DPAA2_CMD_NI_GET_QDID(dev, child, cmd, DPAA2_NI_QUEUE_TX, + &sc->tx_qdid); + if (error) { + device_printf(dev, "%s: failed to get Tx queuing destination " + "ID\n", __func__); + return (error); + } + + return (0); +} + +/** + * @brief Setup ingress traffic distribution. + * + * NOTE: Ingress traffic distribution is valid only when DPNI_OPT_NO_FS option + * hasn't been set for DPNI and a number of DPNI queues > 1. + */ +static int +dpaa2_ni_setup_rx_dist(device_t dev) +{ + /* + * Have the interface implicitly distribute traffic based on the default + * hash key. + */ + return (dpaa2_ni_set_hash(dev, DPAA2_RXH_DEFAULT)); +} + +static int +dpaa2_ni_setup_rx_flow(device_t dev, struct dpaa2_cmd *cmd, + struct dpaa2_ni_fq *fq) +{ + device_t child = dev; + struct dpaa2_devinfo *con_info; + struct dpaa2_ni_queue_cfg queue_cfg = {0}; + int error; + + /* Obtain DPCON associated with the FQ's channel. */ + con_info = device_get_ivars(fq->chan->con_dev); + + queue_cfg.type = DPAA2_NI_QUEUE_RX; + queue_cfg.tc = fq->tc; + queue_cfg.idx = fq->flowid; + error = DPAA2_CMD_NI_GET_QUEUE(dev, child, cmd, &queue_cfg); + if (error) { + device_printf(dev, "%s: failed to obtain Rx queue " + "configuration: tc=%d, flowid=%d\n", __func__, queue_cfg.tc, + queue_cfg.idx); + return (error); + } + + fq->fqid = queue_cfg.fqid; + + queue_cfg.dest_id = con_info->id; + queue_cfg.dest_type = DPAA2_NI_DEST_DPCON; + queue_cfg.priority = 1; + queue_cfg.user_ctx = (uint64_t)(uintmax_t) fq; + queue_cfg.options = + DPAA2_NI_QUEUE_OPT_USER_CTX | + DPAA2_NI_QUEUE_OPT_DEST; + error = DPAA2_CMD_NI_SET_QUEUE(dev, child, cmd, &queue_cfg); + if (error) { + device_printf(dev, "%s: failed to update Rx queue " + "configuration: tc=%d, flowid=%d\n", __func__, queue_cfg.tc, + queue_cfg.idx); + return (error); + } + + if (bootverbose) { + device_printf(dev, "RX queue idx=%d, tc=%d, chan=%d, fqid=%d, " + "user_ctx=%#jx\n", fq->flowid, fq->tc, fq->chan->id, + fq->fqid, (uint64_t) fq); + } + + return (0); +} + +static int +dpaa2_ni_setup_tx_flow(device_t dev, struct dpaa2_cmd *cmd, + struct dpaa2_ni_fq *fq) +{ + device_t child = dev; + struct dpaa2_ni_softc *sc = device_get_softc(dev); + struct dpaa2_devinfo *con_info; + struct dpaa2_ni_queue_cfg queue_cfg = {0}; + struct dpaa2_ni_tx_ring *tx; + struct dpaa2_buf *buf; + uint32_t tx_rings_n = 0; + int error; + + /* Obtain DPCON associated with the FQ's channel. */ + con_info = device_get_ivars(fq->chan->con_dev); + + KASSERT(sc->attr.num.tx_tcs <= DPAA2_NI_MAX_TCS, + ("%s: too many Tx traffic classes: tx_tcs=%d\n", __func__, + sc->attr.num.tx_tcs)); + KASSERT(DPAA2_NI_BUFS_PER_TX <= DPAA2_NI_MAX_BPTX, + ("%s: too many Tx buffers (%d): max=%d\n", __func__, + DPAA2_NI_BUFS_PER_TX, DPAA2_NI_MAX_BPTX)); + + /* Setup Tx rings. */ + for (int i = 0; i < sc->attr.num.tx_tcs; i++) { + queue_cfg.type = DPAA2_NI_QUEUE_TX; + queue_cfg.tc = i; + queue_cfg.idx = fq->flowid; + queue_cfg.chan_id = fq->chan->id; + + error = DPAA2_CMD_NI_GET_QUEUE(dev, child, cmd, &queue_cfg); + if (error) { + device_printf(dev, "%s: failed to obtain Tx queue " + "configuration: tc=%d, flowid=%d\n", __func__, + queue_cfg.tc, queue_cfg.idx); + return (error); + } + + tx = &fq->tx_rings[i]; + tx->fq = fq; + tx->fqid = queue_cfg.fqid; + tx->txid = tx_rings_n; + + if (bootverbose) { + device_printf(dev, "TX queue idx=%d, tc=%d, chan=%d, " + "fqid=%d\n", fq->flowid, i, fq->chan->id, + queue_cfg.fqid); + } + + mtx_init(&tx->lock, "dpaa2_tx_ring", NULL, MTX_DEF); + + /* Allocate Tx ring buffer. */ + tx->idx_br = buf_ring_alloc(DPAA2_TX_BUFRING_SZ, M_DEVBUF, + M_NOWAIT, &tx->lock); + if (tx->idx_br == NULL) { + device_printf(dev, "%s: failed to setup Tx ring buffer" + " (2) fqid=%d\n", __func__, tx->fqid); + return (ENOMEM); + } + + /* Configure Tx buffers. */ + for (uint64_t j = 0; j < DPAA2_NI_BUFS_PER_TX; j++) { + buf = &tx->buf[j]; + buf->type = DPAA2_BUF_TX; + buf->tx.dmat = buf->tx.sgt_dmat = NULL; + buf->tx.dmap = buf->tx.sgt_dmap = NULL; + buf->tx.paddr = buf->tx.sgt_paddr = 0; + buf->tx.vaddr = buf->tx.sgt_vaddr = NULL; + buf->tx.m = NULL; + buf->tx.idx = 0; + + error = dpaa2_ni_seed_txbuf(sc, buf, j); + + /* Add index of the Tx buffer to the ring. */ + buf_ring_enqueue(tx->idx_br, (void *) j); + } + + tx_rings_n++; + } + + /* All Tx queues which belong to the same flowid have the same qdbin. */ + fq->tx_qdbin = queue_cfg.qdbin; + + queue_cfg.type = DPAA2_NI_QUEUE_TX_CONF; + queue_cfg.tc = 0; /* ignored for TxConf queue */ + queue_cfg.idx = fq->flowid; + error = DPAA2_CMD_NI_GET_QUEUE(dev, child, cmd, &queue_cfg); + if (error) { + device_printf(dev, "%s: failed to obtain TxConf queue " + "configuration: tc=%d, flowid=%d\n", __func__, queue_cfg.tc, + queue_cfg.idx); + return (error); + } + + fq->fqid = queue_cfg.fqid; + + queue_cfg.dest_id = con_info->id; + queue_cfg.dest_type = DPAA2_NI_DEST_DPCON; + queue_cfg.priority = 0; + queue_cfg.user_ctx = (uint64_t)(uintmax_t) fq; + queue_cfg.options = + DPAA2_NI_QUEUE_OPT_USER_CTX | + DPAA2_NI_QUEUE_OPT_DEST; + error = DPAA2_CMD_NI_SET_QUEUE(dev, child, cmd, &queue_cfg); + if (error) { + device_printf(dev, "%s: failed to update TxConf queue " + "configuration: tc=%d, flowid=%d\n", __func__, queue_cfg.tc, + queue_cfg.idx); + return (error); + } + + return (0); +} + +static int +dpaa2_ni_setup_rx_err_flow(device_t dev, struct dpaa2_cmd *cmd, + struct dpaa2_ni_fq *fq) +{ + device_t child = dev; + struct dpaa2_devinfo *con_info; + struct dpaa2_ni_queue_cfg queue_cfg = {0}; + int error; + + /* Obtain DPCON associated with the FQ's channel. */ + con_info = device_get_ivars(fq->chan->con_dev); + + queue_cfg.type = DPAA2_NI_QUEUE_RX_ERR; + queue_cfg.tc = fq->tc; /* ignored */ + queue_cfg.idx = fq->flowid; /* ignored */ + error = DPAA2_CMD_NI_GET_QUEUE(dev, child, cmd, &queue_cfg); + if (error) { + device_printf(dev, "%s: failed to obtain RxErr queue " + "configuration\n", __func__); + return (error); + } + + fq->fqid = queue_cfg.fqid; + + queue_cfg.dest_id = con_info->id; + queue_cfg.dest_type = DPAA2_NI_DEST_DPCON; + queue_cfg.priority = 1; + queue_cfg.user_ctx = (uint64_t)(uintmax_t) fq; + queue_cfg.options = + DPAA2_NI_QUEUE_OPT_USER_CTX | + DPAA2_NI_QUEUE_OPT_DEST; + error = DPAA2_CMD_NI_SET_QUEUE(dev, child, cmd, &queue_cfg); + if (error) { + device_printf(dev, "%s: failed to update RxErr queue " + "configuration\n", __func__); + return (error); + } + + return (0); +} + +/** + * @brief Configure DPNI object to generate interrupts. + */ +static int +dpaa2_ni_setup_irqs(device_t dev) +{ + device_t child = dev; + struct dpaa2_ni_softc *sc = device_get_softc(dev); + struct dpaa2_cmd *cmd = sc->cmd; + uint16_t ni_token = sc->ni_token; + int error; + + /* Configure IRQs. */ + error = dpaa2_ni_setup_msi(sc); + if (error) { + device_printf(dev, "%s: failed to allocate MSI\n", __func__); + return (error); + } + if ((sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, + &sc->irq_rid[0], RF_ACTIVE | RF_SHAREABLE)) == NULL) { + device_printf(dev, "%s: failed to allocate IRQ resource\n", + __func__); + return (ENXIO); + } + if (bus_setup_intr(dev, sc->irq_res, INTR_TYPE_NET | INTR_MPSAFE, + NULL, dpaa2_ni_intr, sc, &sc->intr)) { + device_printf(dev, "%s: failed to setup IRQ resource\n", + __func__); + return (ENXIO); + } + + /* Configure DPNI to generate interrupts. */ + error = DPAA2_CMD_NI_SET_IRQ_MASK(dev, child, dpaa2_mcp_tk(cmd, + ni_token), DPNI_IRQ_INDEX, + DPNI_IRQ_LINK_CHANGED | DPNI_IRQ_EP_CHANGED); + if (error) { + device_printf(dev, "%s: failed to set DPNI IRQ mask\n", + __func__); + return (error); + } + + /* Enable IRQ. */ + error = DPAA2_CMD_NI_SET_IRQ_ENABLE(dev, child, cmd, DPNI_IRQ_INDEX, + true); + if (error) { + device_printf(dev, "%s: failed to enable DPNI IRQ\n", __func__); + return (error); + } + + return (0); +} + +/** + * @brief Allocate MSI interrupts for DPNI. + */ +static int +dpaa2_ni_setup_msi(struct dpaa2_ni_softc *sc) +{ + int val; + + val = pci_msi_count(sc->dev); + if (val < DPAA2_NI_MSI_COUNT) + device_printf(sc->dev, "MSI: actual=%d, expected=%d\n", val, + DPAA2_IO_MSI_COUNT); + val = MIN(val, DPAA2_NI_MSI_COUNT); + + if (pci_alloc_msi(sc->dev, &val) != 0) + return (EINVAL); + + for (int i = 0; i < val; i++) + sc->irq_rid[i] = i + 1; + + return (0); +} + +/** + * @brief Update DPNI according to the updated interface capabilities. + */ +static int +dpaa2_ni_setup_if_caps(struct dpaa2_ni_softc *sc) +{ + const bool en_rxcsum = sc->ifp->if_capenable & IFCAP_RXCSUM; + const bool en_txcsum = sc->ifp->if_capenable & IFCAP_TXCSUM; + device_t dev = sc->dev; + device_t child = dev; + int error; + + /* Setup checksums validation. */ + error = DPAA2_CMD_NI_SET_OFFLOAD(dev, child, dpaa2_mcp_tk(sc->cmd, + sc->ni_token), DPAA2_NI_OFL_RX_L3_CSUM, en_rxcsum); + if (error) { + device_printf(dev, "%s: failed to %s L3 checksum validation\n", + __func__, en_rxcsum ? "enable" : "disable"); + return (error); + } + error = DPAA2_CMD_NI_SET_OFFLOAD(dev, child, sc->cmd, + DPAA2_NI_OFL_RX_L4_CSUM, en_rxcsum); + if (error) { + device_printf(dev, "%s: failed to %s L4 checksum validation\n", + __func__, en_rxcsum ? "enable" : "disable"); + return (error); + } + + /* Setup checksums generation. */ + error = DPAA2_CMD_NI_SET_OFFLOAD(dev, child, sc->cmd, + DPAA2_NI_OFL_TX_L3_CSUM, en_txcsum); + if (error) { + device_printf(dev, "%s: failed to %s L3 checksum generation\n", + __func__, en_txcsum ? "enable" : "disable"); + return (error); + } + error = DPAA2_CMD_NI_SET_OFFLOAD(dev, child, sc->cmd, + DPAA2_NI_OFL_TX_L4_CSUM, en_txcsum); + if (error) { + device_printf(dev, "%s: failed to %s L4 checksum generation\n", + __func__, en_txcsum ? "enable" : "disable"); + return (error); + } + + return (0); +} + +/** + * @brief Update DPNI according to the updated interface flags. + */ +static int +dpaa2_ni_setup_if_flags(struct dpaa2_ni_softc *sc) +{ + const bool en_promisc = sc->ifp->if_flags & IFF_PROMISC; + const bool en_allmulti = sc->ifp->if_flags & IFF_ALLMULTI; + device_t dev = sc->dev; + device_t child = dev; + int error; + + error = DPAA2_CMD_NI_SET_MULTI_PROMISC(dev, child, dpaa2_mcp_tk(sc->cmd, + sc->ni_token), en_promisc ? true : en_allmulti); + if (error) { + device_printf(dev, "%s: failed to %s multicast promiscuous " + "mode\n", __func__, en_allmulti ? "enable" : "disable"); + return (error); + } + + error = DPAA2_CMD_NI_SET_UNI_PROMISC(dev, child, sc->cmd, en_promisc); + if (error) { + device_printf(dev, "%s: failed to %s unicast promiscuous mode\n", + __func__, en_promisc ? "enable" : "disable"); + return (error); + } + + return (0); +} + +static int +dpaa2_ni_setup_sysctls(struct dpaa2_ni_softc *sc) +{ + struct sysctl_ctx_list *ctx; + struct sysctl_oid *node, *node2; + struct sysctl_oid_list *parent, *parent2; + char cbuf[128]; + int i; + + ctx = device_get_sysctl_ctx(sc->dev); + parent = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)); + + /* Add DPNI statistics. */ + node = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "stats", + CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "DPNI Statistics"); + parent = SYSCTL_CHILDREN(node); + for (i = 0; i < DPAA2_NI_STAT_SYSCTLS; ++i) { + SYSCTL_ADD_PROC(ctx, parent, i, dpni_stat_sysctls[i].name, + CTLTYPE_U64 | CTLFLAG_RD, sc, 0, dpaa2_ni_collect_stats, + "IU", dpni_stat_sysctls[i].desc); + } + SYSCTL_ADD_UQUAD(ctx, parent, OID_AUTO, "rx_anomaly_frames", + CTLFLAG_RD, &sc->rx_anomaly_frames, + "Rx frames in the buffers outside of the buffer pools"); + SYSCTL_ADD_UQUAD(ctx, parent, OID_AUTO, "rx_single_buf_frames", + CTLFLAG_RD, &sc->rx_single_buf_frames, + "Rx frames in single buffers"); + SYSCTL_ADD_UQUAD(ctx, parent, OID_AUTO, "rx_sg_buf_frames", + CTLFLAG_RD, &sc->rx_sg_buf_frames, + "Rx frames in scatter/gather list"); + SYSCTL_ADD_UQUAD(ctx, parent, OID_AUTO, "rx_enq_rej_frames", + CTLFLAG_RD, &sc->rx_enq_rej_frames, + "Enqueue rejected by QMan"); + SYSCTL_ADD_UQUAD(ctx, parent, OID_AUTO, "rx_ieoi_err_frames", + CTLFLAG_RD, &sc->rx_ieoi_err_frames, + "QMan IEOI error"); + SYSCTL_ADD_UQUAD(ctx, parent, OID_AUTO, "tx_single_buf_frames", + CTLFLAG_RD, &sc->tx_single_buf_frames, + "Tx single buffer frames"); + SYSCTL_ADD_UQUAD(ctx, parent, OID_AUTO, "tx_sg_frames", + CTLFLAG_RD, &sc->tx_sg_frames, + "Tx S/G frames"); + + SYSCTL_ADD_PROC(ctx, parent, OID_AUTO, "buf_num", + CTLTYPE_U32 | CTLFLAG_RD, sc, 0, dpaa2_ni_collect_buf_num, + "IU", "number of Rx buffers in the buffer pool"); + SYSCTL_ADD_PROC(ctx, parent, OID_AUTO, "buf_free", + CTLTYPE_U32 | CTLFLAG_RD, sc, 0, dpaa2_ni_collect_buf_free, + "IU", "number of free Rx buffers in the buffer pool"); + + parent = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)); + + /* Add channels statistics. */ + node = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "channels", + CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "DPNI Channels"); + parent = SYSCTL_CHILDREN(node); + for (int i = 0; i < sc->chan_n; i++) { + snprintf(cbuf, sizeof(cbuf), "%d", i); + + node2 = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, cbuf, + CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "DPNI Channel"); + parent2 = SYSCTL_CHILDREN(node2); + + SYSCTL_ADD_UQUAD(ctx, parent2, OID_AUTO, "tx_frames", + CTLFLAG_RD, &sc->channels[i]->tx_frames, + "Tx frames counter"); + SYSCTL_ADD_UQUAD(ctx, parent2, OID_AUTO, "tx_dropped", + CTLFLAG_RD, &sc->channels[i]->tx_dropped, + "Tx dropped counter"); + } + + return (0); +} + +static int +dpaa2_ni_setup_dma(struct dpaa2_ni_softc *sc) +{ + device_t dev = sc->dev; + int error; + + KASSERT((sc->buf_align == BUF_ALIGN) || (sc->buf_align == BUF_ALIGN_V1), + ("unexpected buffer alignment: %d\n", sc->buf_align)); + + /* + * DMA tag to allocate buffers for buffer pool. + * + * NOTE: QBMan supports DMA addresses up to 49-bits maximum. + * Bits 63-49 are not used by QBMan. + */ + error = bus_dma_tag_create( + bus_get_dma_tag(dev), + sc->buf_align, 0, /* alignment, boundary */ + BUF_MAXADDR_49BIT, /* low restricted addr */ + BUF_MAXADDR, /* high restricted addr */ + NULL, NULL, /* filter, filterarg */ + BUF_SIZE, 1, /* maxsize, nsegments */ + BUF_SIZE, 0, /* maxsegsize, flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc->bp_dmat); + if (error) { + device_printf(dev, "%s: failed to create DMA tag for buffer " + "pool\n", __func__); + return (error); + } + + /* DMA tag to map Tx mbufs. */ + error = bus_dma_tag_create( + bus_get_dma_tag(dev), + sc->buf_align, 0, /* alignment, boundary */ + BUF_MAXADDR_49BIT, /* low restricted addr */ + BUF_MAXADDR, /* high restricted addr */ + NULL, NULL, /* filter, filterarg */ + DPAA2_TX_SEGS_MAXSZ, /* maxsize */ + DPAA2_TX_SEGLIMIT, /* nsegments */ + DPAA2_TX_SEG_SZ, 0, /* maxsegsize, flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc->tx_dmat); + if (error) { + device_printf(dev, "%s: failed to create DMA tag for Tx " + "buffers\n", __func__); + return (error); + } + + /* DMA tag to allocate channel storage. */ + error = bus_dma_tag_create( + bus_get_dma_tag(dev), + ETH_STORE_ALIGN, 0, /* alignment, boundary */ + BUS_SPACE_MAXADDR_32BIT, /* low restricted addr */ + BUS_SPACE_MAXADDR, /* high restricted addr */ + NULL, NULL, /* filter, filterarg */ + ETH_STORE_SIZE, 1, /* maxsize, nsegments */ + ETH_STORE_SIZE, 0, /* maxsegsize, flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc->st_dmat); + if (error) { + device_printf(dev, "%s: failed to create DMA tag for channel " + "storage\n", __func__); + return (error); + } + + /* DMA tag for Rx distribution key. */ + error = bus_dma_tag_create( + bus_get_dma_tag(dev), + PAGE_SIZE, 0, /* alignment, boundary */ + BUS_SPACE_MAXADDR_32BIT, /* low restricted addr */ + BUS_SPACE_MAXADDR, /* high restricted addr */ + NULL, NULL, /* filter, filterarg */ + DPAA2_CLASSIFIER_DMA_SIZE, 1, /* maxsize, nsegments */ + DPAA2_CLASSIFIER_DMA_SIZE, 0, /* maxsegsize, flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc->rxd_dmat); + if (error) { + device_printf(dev, "%s: failed to create DMA tag for Rx " + "distribution key\n", __func__); + return (error); + } + + error = bus_dma_tag_create( + bus_get_dma_tag(dev), + PAGE_SIZE, 0, /* alignment, boundary */ + BUS_SPACE_MAXADDR_32BIT, /* low restricted addr */ + BUS_SPACE_MAXADDR, /* high restricted addr */ + NULL, NULL, /* filter, filterarg */ + ETH_QOS_KCFG_BUF_SIZE, 1, /* maxsize, nsegments */ + ETH_QOS_KCFG_BUF_SIZE, 0, /* maxsegsize, flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc->qos_dmat); + if (error) { + device_printf(dev, "%s: failed to create DMA tag for QoS key\n", + __func__); + return (error); + } + + error = bus_dma_tag_create( + bus_get_dma_tag(dev), + PAGE_SIZE, 0, /* alignment, boundary */ + BUS_SPACE_MAXADDR_32BIT, /* low restricted addr */ + BUS_SPACE_MAXADDR, /* high restricted addr */ + NULL, NULL, /* filter, filterarg */ + DPAA2_TX_SGT_SZ, 1, /* maxsize, nsegments */ + DPAA2_TX_SGT_SZ, 0, /* maxsegsize, flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc->sgt_dmat); + if (error) { + device_printf(dev, "%s: failed to create DMA tag for S/G " + "tables\n", __func__); + return (error); + } + + return (0); +} + +/** + * @brief Configure buffer layouts of the different DPNI queues. + */ +static int +dpaa2_ni_set_buf_layout(device_t dev, struct dpaa2_cmd *cmd) +{ + device_t child = dev; + struct dpaa2_ni_softc *sc = device_get_softc(dev); + struct dpaa2_ni_buf_layout buf_layout = {0}; + int error; + + /* + * Select Rx/Tx buffer alignment. It's necessary to ensure that the + * buffer size seen by WRIOP is a multiple of 64 or 256 bytes depending + * on the WRIOP version. + */ + sc->buf_align = (sc->attr.wriop_ver == WRIOP_VERSION(0, 0, 0) || + sc->attr.wriop_ver == WRIOP_VERSION(1, 0, 0)) + ? BUF_ALIGN_V1 : BUF_ALIGN; + + /* + * We need to ensure that the buffer size seen by WRIOP is a multiple + * of 64 or 256 bytes depending on the WRIOP version. + */ + sc->buf_sz = ALIGN_DOWN(BUF_SIZE, sc->buf_align); + + if (bootverbose) + device_printf(dev, "Rx/Tx buffers: size=%d, alignment=%d\n", + sc->buf_sz, sc->buf_align); + + /* + * Frame Descriptor Tx buffer layout + * + * ADDR -> |---------------------| + * | SW FRAME ANNOTATION | BUF_SWA_SIZE bytes + * |---------------------| + * | HW FRAME ANNOTATION | BUF_TX_HWA_SIZE bytes + * |---------------------| + * | DATA HEADROOM | + * ADDR + OFFSET -> |---------------------| + * | | + * | | + * | FRAME DATA | + * | | + * | | + * |---------------------| + * | DATA TAILROOM | + * |---------------------| + * + * NOTE: It's for a single buffer frame only. + */ + buf_layout.queue_type = DPAA2_NI_QUEUE_TX; + buf_layout.pd_size = BUF_SWA_SIZE; + buf_layout.pass_timestamp = true; + buf_layout.pass_frame_status = true; + buf_layout.options = + BUF_LOPT_PRIV_DATA_SZ | + BUF_LOPT_TIMESTAMP | /* requires 128 bytes in HWA */ + BUF_LOPT_FRAME_STATUS; + error = DPAA2_CMD_NI_SET_BUF_LAYOUT(dev, child, cmd, &buf_layout); + if (error) { + device_printf(dev, "%s: failed to set Tx buffer layout\n", + __func__); + return (error); + } + + /* Tx-confirmation buffer layout */ + buf_layout.queue_type = DPAA2_NI_QUEUE_TX_CONF; + buf_layout.options = + BUF_LOPT_TIMESTAMP | + BUF_LOPT_FRAME_STATUS; + error = DPAA2_CMD_NI_SET_BUF_LAYOUT(dev, child, cmd, &buf_layout); + if (error) { + device_printf(dev, "%s: failed to set TxConf buffer layout\n", + __func__); + return (error); + } + + /* + * Driver should reserve the amount of space indicated by this command + * as headroom in all Tx frames. + */ + error = DPAA2_CMD_NI_GET_TX_DATA_OFF(dev, child, cmd, &sc->tx_data_off); + if (error) { + device_printf(dev, "%s: failed to obtain Tx data offset\n", + __func__); + return (error); + } + + if (bootverbose) + device_printf(dev, "Tx data offset=%d\n", sc->tx_data_off); + if ((sc->tx_data_off % 64) != 0) + device_printf(dev, "Tx data offset (%d) is not a multiplication " + "of 64 bytes\n", sc->tx_data_off); + + /* + * Frame Descriptor Rx buffer layout + * + * ADDR -> |---------------------| + * | SW FRAME ANNOTATION | 0 bytes + * |---------------------| + * | HW FRAME ANNOTATION | BUF_RX_HWA_SIZE bytes + * |---------------------| + * | DATA HEADROOM | OFFSET-BUF_RX_HWA_SIZE + * ADDR + OFFSET -> |---------------------| + * | | + * | | + * | FRAME DATA | + * | | + * | | + * |---------------------| + * | DATA TAILROOM | 0 bytes + * |---------------------| + * + * NOTE: It's for a single buffer frame only. + */ + buf_layout.queue_type = DPAA2_NI_QUEUE_RX; + buf_layout.pd_size = 0; + buf_layout.fd_align = sc->buf_align; + buf_layout.head_size = sc->tx_data_off - BUF_RX_HWA_SIZE; + buf_layout.tail_size = 0; + buf_layout.pass_frame_status = true; + buf_layout.pass_parser_result = true; + buf_layout.pass_timestamp = true; + buf_layout.options = + BUF_LOPT_PRIV_DATA_SZ | + BUF_LOPT_DATA_ALIGN | + BUF_LOPT_DATA_HEAD_ROOM | + BUF_LOPT_DATA_TAIL_ROOM | + BUF_LOPT_FRAME_STATUS | + BUF_LOPT_PARSER_RESULT | + BUF_LOPT_TIMESTAMP; + error = DPAA2_CMD_NI_SET_BUF_LAYOUT(dev, child, cmd, &buf_layout); + if (error) { + device_printf(dev, "%s: failed to set Rx buffer layout\n", + __func__); + return (error); + } + + return (0); +} + +/** + * @brief Enable Rx/Tx pause frames. + * + * NOTE: DPNI stops sending when a pause frame is received (Rx frame) or DPNI + * itself generates pause frames (Tx frame). + */ +static int +dpaa2_ni_set_pause_frame(device_t dev, struct dpaa2_cmd *cmd) +{ + device_t child = dev; + struct dpaa2_ni_softc *sc = device_get_softc(dev); + struct dpaa2_ni_link_cfg link_cfg = {0}; + int error; + + error = DPAA2_CMD_NI_GET_LINK_CFG(dev, child, cmd, &link_cfg); + if (error) { + device_printf(dev, "%s: failed to obtain link configuration: " + "error=%d\n", __func__, error); + return (error); + } + + /* Enable both Rx and Tx pause frames by default. */ + link_cfg.options |= DPAA2_NI_LINK_OPT_PAUSE; + link_cfg.options &= ~DPAA2_NI_LINK_OPT_ASYM_PAUSE; + + error = DPAA2_CMD_NI_SET_LINK_CFG(dev, child, cmd, &link_cfg); + if (error) { + device_printf(dev, "%s: failed to set link configuration: " + "error=%d\n", __func__, error); + return (error); + } + + sc->link_options = link_cfg.options; + + return (0); +} + +/** + * @brief Configure QoS table to determine the traffic class for the received + * frame. + */ +static int +dpaa2_ni_set_qos_table(device_t dev, struct dpaa2_cmd *cmd) +{ + device_t child = dev; + struct dpaa2_ni_softc *sc = device_get_softc(dev); + struct dpaa2_ni_qos_table tbl; + struct dpaa2_buf *buf = &sc->qos_kcfg; + int error; + + if (sc->attr.num.rx_tcs == 1 || + !(sc->attr.options & DPNI_OPT_HAS_KEY_MASKING)) { + if (bootverbose) + device_printf(dev, "Ingress traffic classification is " + "not supported\n"); + return (0); + } + + /* + * Allocate a buffer visible to the device to hold the QoS table key + * configuration. + */ + KASSERT(buf->type == DPAA2_BUF_STORE, ("%s: not storage buffer", + __func__)); + if (__predict_true(buf->store.dmat == NULL)) + buf->store.dmat = sc->qos_dmat; + + error = bus_dmamem_alloc(buf->store.dmat, &buf->store.vaddr, + BUS_DMA_ZERO | BUS_DMA_COHERENT, &buf->store.dmap); + if (error) { + device_printf(dev, "%s: failed to allocate a buffer for QoS key " + "configuration\n", __func__); + return (error); + } + + error = bus_dmamap_load(buf->store.dmat, buf->store.dmap, + buf->store.vaddr, ETH_QOS_KCFG_BUF_SIZE, dpaa2_ni_dmamap_cb, + &buf->store.paddr, BUS_DMA_NOWAIT); + if (error) { + device_printf(dev, "%s: failed to map QoS key configuration " + "buffer into bus space\n", __func__); + return (error); + } + + tbl.default_tc = 0; + tbl.discard_on_miss = false; + tbl.keep_entries = false; + tbl.kcfg_busaddr = buf->store.paddr; + error = DPAA2_CMD_NI_SET_QOS_TABLE(dev, child, cmd, &tbl); + if (error) { + device_printf(dev, "%s: failed to set QoS table\n", __func__); + return (error); + } + + error = DPAA2_CMD_NI_CLEAR_QOS_TABLE(dev, child, cmd); + if (error) { + device_printf(dev, "%s: failed to clear QoS table\n", __func__); + return (error); + } + + return (0); +} + +static int +dpaa2_ni_set_mac_addr(device_t dev, struct dpaa2_cmd *cmd, uint16_t rc_token, + uint16_t ni_token) +{ + device_t child = dev; + struct dpaa2_ni_softc *sc = device_get_softc(dev); + struct ifnet *ifp = sc->ifp; + struct ether_addr rnd_mac_addr; + uint8_t mac_addr[ETHER_ADDR_LEN]; + uint8_t dpni_mac_addr[ETHER_ADDR_LEN]; + int error; + + /* + * Get the MAC address associated with the physical port, if the DPNI is + * connected to a DPMAC directly associated with one of the physical + * ports. + */ + error = DPAA2_CMD_NI_GET_PORT_MAC_ADDR(dev, child, dpaa2_mcp_tk(cmd, + ni_token), mac_addr); + if (error) { + device_printf(dev, "%s: failed to obtain the MAC address " + "associated with the physical port\n", __func__); + return (error); + } + + /* Get primary MAC address from the DPNI attributes. */ + error = DPAA2_CMD_NI_GET_PRIM_MAC_ADDR(dev, child, cmd, dpni_mac_addr); + if (error) { + device_printf(dev, "%s: failed to obtain primary MAC address\n", + __func__); + return (error); + } + + if (!ETHER_IS_ZERO(mac_addr)) { + /* Set MAC address of the physical port as DPNI's primary one. */ + error = DPAA2_CMD_NI_SET_PRIM_MAC_ADDR(dev, child, cmd, + mac_addr); + if (error) { + device_printf(dev, "%s: failed to set primary MAC " + "address\n", __func__); + return (error); + } + for (int i = 0; i < ETHER_ADDR_LEN; i++) + sc->mac.addr[i] = mac_addr[i]; + } else if (ETHER_IS_ZERO(dpni_mac_addr)) { + /* Generate random MAC address as DPNI's primary one. */ + ether_gen_addr(ifp, &rnd_mac_addr); + for (int i = 0; i < ETHER_ADDR_LEN; i++) + mac_addr[i] = rnd_mac_addr.octet[i]; + + error = DPAA2_CMD_NI_SET_PRIM_MAC_ADDR(dev, child, cmd, + mac_addr); + if (error) { + device_printf(dev, "%s: failed to set random primary " + "MAC address\n", __func__); + return (error); + } + for (int i = 0; i < ETHER_ADDR_LEN; i++) + sc->mac.addr[i] = mac_addr[i]; + } else { + for (int i = 0; i < ETHER_ADDR_LEN; i++) + sc->mac.addr[i] = dpni_mac_addr[i]; + } + + return (0); +} + +static void +dpaa2_ni_miibus_statchg(device_t dev) +{ + struct dpaa2_ni_softc *sc; + device_t child; + struct dpaa2_mac_link_state mac_link = { 0 }; + uint16_t mac_token; + int error, link_state; + + sc = device_get_softc(dev); + if (sc->fixed_link || sc->mii == NULL) + return; + + /* + * Note: ifp link state will only be changed AFTER we are called so we + * cannot rely on ifp->if_linkstate here. + */ + if (sc->mii->mii_media_status & IFM_AVALID) { + if (sc->mii->mii_media_status & IFM_ACTIVE) + link_state = LINK_STATE_UP; + else + link_state = LINK_STATE_DOWN; + } else + link_state = LINK_STATE_UNKNOWN; + + if (link_state != sc->link_state) { + + sc->link_state = link_state; + + child = sc->dev; + error = DPAA2_CMD_MAC_OPEN(sc->dev, child, dpaa2_mcp_tk(sc->cmd, + sc->rc_token), sc->mac.dpmac_id, &mac_token); + if (error) { + device_printf(sc->dev, "%s: failed to open DPMAC: " + "id=%d, error=%d\n", __func__, sc->mac.dpmac_id, + error); + return; + } + + if (link_state == LINK_STATE_UP || + link_state == LINK_STATE_DOWN) { + /* Update DPMAC link state. */ + mac_link.supported = sc->mii->mii_media.ifm_media; + mac_link.advert = sc->mii->mii_media.ifm_media; + mac_link.rate = 1000; /* TODO: Where to get from? */ /* ifmedia_baudrate? */ + mac_link.options = + DPAA2_MAC_LINK_OPT_AUTONEG | + DPAA2_MAC_LINK_OPT_PAUSE; + mac_link.up = (link_state == LINK_STATE_UP) ? true : false; + mac_link.state_valid = true; + + /* Inform DPMAC about link state. */ + error = DPAA2_CMD_MAC_SET_LINK_STATE(sc->dev, child, + sc->cmd, &mac_link); + if (error) + device_printf(sc->dev, "%s: failed to set DPMAC " + "link state: id=%d, error=%d\n", __func__, + sc->mac.dpmac_id, error); + } + DPAA2_CMD_MAC_CLOSE(sc->dev, child, dpaa2_mcp_tk(sc->cmd, + mac_token)); + } +} + +/** + * @brief Callback function to process media change request. + */ +static int +dpaa2_ni_media_change(struct ifnet *ifp) +{ + struct dpaa2_ni_softc *sc = ifp->if_softc; + + DPNI_LOCK(sc); + if (sc->mii) { + mii_mediachg(sc->mii); + sc->media_status = sc->mii->mii_media.ifm_media; + } else if (sc->fixed_link) { + if_printf(ifp, "%s: can't change media in fixed mode\n", + __func__); + } + DPNI_UNLOCK(sc); + + return (0); +} + +/** + * @brief Callback function to process media status request. + */ +static void +dpaa2_ni_media_status(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + struct dpaa2_ni_softc *sc = ifp->if_softc; + + DPNI_LOCK(sc); + if (sc->mii) { + mii_pollstat(sc->mii); + ifmr->ifm_active = sc->mii->mii_media_active; + ifmr->ifm_status = sc->mii->mii_media_status; + } + DPNI_UNLOCK(sc); +} + +/** + * @brief Callout function to check and update media status. + */ +static void +dpaa2_ni_media_tick(void *arg) +{ + struct dpaa2_ni_softc *sc = (struct dpaa2_ni_softc *) arg; + + /* Check for media type change */ + if (sc->mii) { + mii_tick(sc->mii); + if (sc->media_status != sc->mii->mii_media.ifm_media) { + printf("%s: media type changed (ifm_media=%x)\n", + __func__, sc->mii->mii_media.ifm_media); + dpaa2_ni_media_change(sc->ifp); + } + } + + /* Schedule another timeout one second from now */ + callout_reset(&sc->mii_callout, hz, dpaa2_ni_media_tick, sc); +} + +static void +dpaa2_ni_init(void *arg) +{ + struct dpaa2_ni_softc *sc = (struct dpaa2_ni_softc *) arg; + struct ifnet *ifp = sc->ifp; + device_t dev = sc->dev; + device_t child = dev; + int error; + + DPNI_LOCK(sc); + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { + DPNI_UNLOCK(sc); + return; + } + DPNI_UNLOCK(sc); + + error = DPAA2_CMD_NI_ENABLE(dev, child, dpaa2_mcp_tk(sc->cmd, + sc->ni_token)); + if (error) + device_printf(dev, "%s: failed to enable DPNI: error=%d\n", + __func__, error); + + DPNI_LOCK(sc); + if (sc->mii) + mii_mediachg(sc->mii); + callout_reset(&sc->mii_callout, hz, dpaa2_ni_media_tick, sc); + + ifp->if_drv_flags |= IFF_DRV_RUNNING; + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + DPNI_UNLOCK(sc); + + /* Force link-state update to initilize things. */ + dpaa2_ni_miibus_statchg(dev); + + return; +} + +static int +dpaa2_ni_transmit(struct ifnet *ifp, struct mbuf *m) +{ + struct dpaa2_ni_softc *sc = ifp->if_softc; + struct dpaa2_ni_channel *chan; + struct dpaa2_ni_tx_ring *tx; + uint32_t fqid; + boolean_t found = false; + int chan_n = 0; + + if (__predict_false(!(ifp->if_drv_flags & IFF_DRV_RUNNING))) + return (0); + + if (M_HASHTYPE_GET(m) != M_HASHTYPE_NONE) { + fqid = m->m_pkthdr.flowid; + for (int i = 0; i < sc->chan_n; i++) { + chan = sc->channels[i]; + for (int j = 0; j < chan->rxq_n; j++) { + if (fqid == chan->rx_queues[j].fqid) { + chan_n = chan->flowid; + found = true; + break; + } + } + if (found) { + break; + } + } + } + tx = DPAA2_TX_RING(sc, chan_n, 0); + + TX_LOCK(tx); + dpaa2_ni_tx_locked(sc, tx, m); + TX_UNLOCK(tx); + + return (0); +} + +static void +dpaa2_ni_qflush(struct ifnet *ifp) +{ + /* TODO: Find a way to drain Tx queues in QBMan. */ + if_qflush(ifp); +} + +static int +dpaa2_ni_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct dpaa2_ni_softc *sc = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *) data; + device_t dev, child; + uint32_t changed = 0; + int mtu, error, rc = 0; + + dev = child = sc->dev; + + switch (cmd) { + case SIOCSIFMTU: + DPNI_LOCK(sc); + mtu = ifr->ifr_mtu; + if (mtu < ETHERMIN || mtu > ETHERMTU_JUMBO) { + DPNI_UNLOCK(sc); + return (EINVAL); + } + ifp->if_mtu = mtu; + DPNI_UNLOCK(sc); + + /* Update maximum frame length. */ + error = DPAA2_CMD_NI_SET_MFL(dev, child, dpaa2_mcp_tk(sc->cmd, + sc->ni_token), mtu + ETHER_HDR_LEN); + if (error) { + device_printf(dev, "%s: failed to update maximum frame " + "length: error=%d\n", __func__, error); + return (error); + } + break; + case SIOCSIFCAP: + changed = ifp->if_capenable ^ ifr->ifr_reqcap; + if (changed & IFCAP_HWCSUM) { + if ((ifr->ifr_reqcap & changed) & IFCAP_HWCSUM) + ifp->if_capenable |= IFCAP_HWCSUM; + else + ifp->if_capenable &= ~IFCAP_HWCSUM; + } + rc = dpaa2_ni_setup_if_caps(sc); + if (rc) { + printf("%s: failed to update iface capabilities: " + "error=%d\n", __func__, rc); + rc = ENXIO; + } + break; + case SIOCSIFFLAGS: + DPNI_LOCK(sc); + if (ifp->if_flags & IFF_UP) { + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + changed = ifp->if_flags ^ sc->if_flags; + if (changed & IFF_PROMISC || + changed & IFF_ALLMULTI) { + rc = dpaa2_ni_setup_if_flags(sc); + } + } else { + DPNI_UNLOCK(sc); + dpaa2_ni_init(sc); + DPNI_LOCK(sc); + } + } else if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + /* dpni_if_stop(sc); */ + } + + sc->if_flags = ifp->if_flags; + DPNI_UNLOCK(sc); + break; + case SIOCADDMULTI: + case SIOCDELMULTI: + DPNI_LOCK(sc); + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + DPNI_UNLOCK(sc); + rc = dpaa2_ni_update_mac_filters(ifp); + if (rc) + device_printf(dev, "%s: failed to update MAC " + "filters: error=%d\n", __func__, rc); + DPNI_LOCK(sc); + } + DPNI_UNLOCK(sc); + break; + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + if (sc->mii) + rc = ifmedia_ioctl(ifp, ifr, &sc->mii->mii_media, cmd); + else if(sc->fixed_link) { + rc = ifmedia_ioctl(ifp, ifr, &sc->fixed_ifmedia, cmd); + } + break; + default: + rc = ether_ioctl(ifp, cmd, data); + } + + return (rc); +} + +static int +dpaa2_ni_update_mac_filters(struct ifnet *ifp) +{ + struct dpaa2_ni_softc *sc = ifp->if_softc; + struct dpaa2_ni_mcaddr_ctx ctx; + device_t dev, child; + int error; + + dev = child = sc->dev; + + /* Remove all multicast MAC filters. */ + error = DPAA2_CMD_NI_CLEAR_MAC_FILTERS(dev, child, dpaa2_mcp_tk(sc->cmd, + sc->ni_token), false, true); + if (error) { + device_printf(dev, "%s: failed to clear multicast MAC filters: " + "error=%d\n", __func__, error); + return (error); + } + + ctx.ifp = ifp; + ctx.error = 0; + ctx.nent = 0; + + if_foreach_llmaddr(ifp, dpaa2_ni_add_maddr, &ctx); + + return (ctx.error); +} + +static u_int +dpaa2_ni_add_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt) +{ + struct dpaa2_ni_mcaddr_ctx *ctx = arg; + struct dpaa2_ni_softc *sc = ctx->ifp->if_softc; + device_t dev, child; + + dev = child = sc->dev; + + if (ctx->error != 0) + return (0); + + if (ETHER_IS_MULTICAST(LLADDR(sdl))) { + ctx->error = DPAA2_CMD_NI_ADD_MAC_ADDR(dev, child, dpaa2_mcp_tk( + sc->cmd, sc->ni_token), LLADDR(sdl)); + if (ctx->error != 0) { + device_printf(dev, "%s: can't add more then %d MAC " + "addresses, switching to the multicast promiscuous " + "mode\n", __func__, ctx->nent); + + /* Enable multicast promiscuous mode. */ + DPNI_LOCK(sc); + ctx->ifp->if_flags |= IFF_ALLMULTI; + sc->if_flags |= IFF_ALLMULTI; + ctx->error = dpaa2_ni_setup_if_flags(sc); + DPNI_UNLOCK(sc); + + return (0); + } + ctx->nent++; + } + + return (1); +} + +static void +dpaa2_ni_intr(void *arg) +{ + struct dpaa2_ni_softc *sc = (struct dpaa2_ni_softc *) arg; + device_t child = sc->dev; + uint32_t status = ~0u; /* clear all IRQ status bits */ + int error; + + error = DPAA2_CMD_NI_GET_IRQ_STATUS(sc->dev, child, dpaa2_mcp_tk(sc->cmd, + sc->ni_token), DPNI_IRQ_INDEX, &status); + if (error) + device_printf(sc->dev, "%s: failed to obtain IRQ status: " + "error=%d\n", __func__, error); +} + +/** + * @brief Callback to obtain a physical address of the only DMA segment mapped. + */ +static void +dpaa2_ni_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error) +{ + if (error == 0) { + KASSERT(nseg == 1, ("too many segments: nseg=%d\n", nseg)); + *(bus_addr_t *) arg = segs[0].ds_addr; + } +} + +/** + * @brief Release new buffers to the buffer pool if necessary. + */ +static void +dpaa2_ni_bp_task(void *arg, int count) +{ + device_t bp_dev; + struct dpaa2_ni_softc *sc = (struct dpaa2_ni_softc *) arg; + struct dpaa2_bp_softc *bpsc; + struct dpaa2_bp_conf bp_conf; + const int buf_num = DPAA2_ATOMIC_READ(&sc->buf_num); + int error; + + /* There's only one buffer pool for now. */ + bp_dev = (device_t) rman_get_start(sc->res[BP_RID(0)]); + bpsc = device_get_softc(bp_dev); + + /* Get state of the buffer pool. */ + error = DPAA2_SWP_QUERY_BP(sc->channels[0]->io_dev, bpsc->attr.bpid, + &bp_conf); + if (error) { + device_printf(sc->dev, "%s: failed to query buffer pool " + "configuration: error=%d\n", __func__, error); + return; + } + + /* Double allocated buffers number if free buffers < 25%. */ + if (bp_conf.free_bufn < (buf_num >> 2)) { + (void)dpaa2_ni_seed_buf_pool(sc, buf_num); + DPAA2_ATOMIC_XCHG(&sc->buf_free, bp_conf.free_bufn); + } +} + +/** + * @brief Poll frames from a specific channel when CDAN is received. + * + * NOTE: To be called from the DPIO interrupt handler. + */ +static void +dpaa2_ni_poll(void *arg) +{ + struct dpaa2_ni_channel *chan = (struct dpaa2_ni_channel *) arg; + struct dpaa2_io_softc *iosc; + struct dpaa2_swp *swp; + struct dpaa2_ni_fq *fq; + int error, consumed = 0; + + KASSERT(chan != NULL, ("%s: channel is NULL", __func__)); + + iosc = device_get_softc(chan->io_dev); + swp = iosc->swp; + + do { + error = dpaa2_swp_pull(swp, chan->id, &chan->store, + ETH_STORE_FRAMES); + if (error) { + device_printf(chan->ni_dev, "%s: failed to pull frames: " + "chan_id=%d, error=%d\n", __func__, chan->id, error); + break; + } + + /* + * TODO: Combine frames from the same Rx queue returned as + * a result to the current VDQ command into a chain (linked + * with m_nextpkt) to ammortize the FQ lock. + */ + error = dpaa2_ni_consume_frames(chan, &fq, &consumed); + if (error == ENOENT) { + break; + } + if (error == ETIMEDOUT) { + device_printf(chan->ni_dev, "%s: timeout to consume " + "frames: chan_id=%d\n", __func__, chan->id); + } + } while (true); + + /* Re-arm channel to generate CDAN. */ + error = DPAA2_SWP_CONF_WQ_CHANNEL(chan->io_dev, &chan->ctx); + if (error) { + device_printf(chan->ni_dev, "%s: failed to rearm: chan_id=%d, " + "error=%d\n", __func__, chan->id, error); + } +} + +/** + * @brief Transmit mbufs. + */ +static void +dpaa2_ni_tx_locked(struct dpaa2_ni_softc *sc, struct dpaa2_ni_tx_ring *tx, + struct mbuf *m) +{ + struct dpaa2_ni_fq *fq = tx->fq; + struct dpaa2_buf *buf; + struct dpaa2_fd fd; + struct mbuf *m_d; + bus_dma_segment_t txsegs[DPAA2_TX_SEGLIMIT]; + uint64_t idx; + void *pidx; + int error, rc, txnsegs; + + /* Obtain an index of a Tx buffer. */ + pidx = buf_ring_dequeue_sc(tx->idx_br); + if (__predict_false(pidx == NULL)) { + /* TODO: Do not give up easily. */ + m_freem(m); + return; + } else { + idx = (uint64_t) pidx; + buf = &tx->buf[idx]; + buf->tx.m = m; + buf->tx.idx = idx; + buf->tx.sgt_paddr = 0; + } + + /* Load mbuf to transmit. */ + error = bus_dmamap_load_mbuf_sg(buf->tx.dmat, buf->tx.dmap, m, + txsegs, &txnsegs, BUS_DMA_NOWAIT); + if (__predict_false(error != 0)) { + /* Too many fragments, trying to defragment... */ + m_d = m_collapse(m, M_NOWAIT, DPAA2_TX_SEGLIMIT); + if (m_d == NULL) { + device_printf(sc->dev, "%s: mbuf " + "defragmentation failed\n", __func__); + fq->chan->tx_dropped++; + goto err; + } + + buf->tx.m = m = m_d; + error = bus_dmamap_load_mbuf_sg(buf->tx.dmat, + buf->tx.dmap, m, txsegs, &txnsegs, BUS_DMA_NOWAIT); + if (__predict_false(error != 0)) { + device_printf(sc->dev, "%s: failed to load " + "mbuf: error=%d\n", __func__, error); + fq->chan->tx_dropped++; + goto err; + } + } + + /* Build frame descriptor. */ + error = dpaa2_ni_build_fd(sc, tx, buf, txsegs, txnsegs, &fd); + if (__predict_false(error != 0)) { + device_printf(sc->dev, "%s: failed to build frame " + "descriptor: error=%d\n", __func__, error); + fq->chan->tx_dropped++; + goto err_unload; + } + + /* TODO: Enqueue several frames in a single command. */ + for (int i = 0; i < DPAA2_NI_ENQUEUE_RETRIES; i++) { + /* TODO: Return error codes instead of # of frames. */ + rc = DPAA2_SWP_ENQ_MULTIPLE_FQ(fq->chan->io_dev, tx->fqid, + &fd, 1); + if (rc == 1) { + break; + } + } + + bus_dmamap_sync(buf->tx.dmat, buf->tx.dmap, + BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(buf->tx.sgt_dmat, buf->tx.sgt_dmap, + BUS_DMASYNC_PREWRITE); + + if (rc != 1) { + fq->chan->tx_dropped++; + goto err_unload; + } else { + fq->chan->tx_frames++; + } + return; + +err_unload: + bus_dmamap_unload(buf->tx.dmat, buf->tx.dmap); + if (buf->tx.sgt_paddr != 0) { + bus_dmamap_unload(buf->tx.sgt_dmat, buf->tx.sgt_dmap); + } +err: + m_freem(buf->tx.m); + buf_ring_enqueue(tx->idx_br, pidx); +} + +static int +dpaa2_ni_consume_frames(struct dpaa2_ni_channel *chan, struct dpaa2_ni_fq **src, + uint32_t *consumed) +{ + struct dpaa2_ni_fq *fq = NULL; + struct dpaa2_dq *dq; + struct dpaa2_fd *fd; + int rc, frames = 0; + + do { + rc = dpaa2_ni_chan_storage_next(chan, &dq); + if (rc == EINPROGRESS) { + if (dq != NULL && !IS_NULL_RESPONSE(dq->fdr.desc.stat)) { + fd = &dq->fdr.fd; + fq = (struct dpaa2_ni_fq *) dq->fdr.desc.fqd_ctx; + fq->consume(chan, fq, fd); + frames++; + } + } else if (rc == EALREADY || rc == ENOENT) { + if (dq != NULL && !IS_NULL_RESPONSE(dq->fdr.desc.stat)) { + fd = &dq->fdr.fd; + fq = (struct dpaa2_ni_fq *) dq->fdr.desc.fqd_ctx; + fq->consume(chan, fq, fd); + frames++; + } + break; + } else { + KASSERT(1 == 0, ("%s: should not reach here", __func__)); + } + } while (true); + + KASSERT(chan->store_idx < chan->store_sz, + ("channel store idx >= size: store_idx=%d, store_sz=%d", + chan->store_idx, chan->store_sz)); + + /* + * A dequeue operation pulls frames from a single queue into the store. + * Return the frame queue and a number of consumed frames as an output. + */ + if (src != NULL) + *src = fq; + if (consumed != NULL) + *consumed = frames; + + return (rc); +} + +/** + * @brief Receive frames. + */ +static int +dpaa2_ni_rx(struct dpaa2_ni_channel *chan, struct dpaa2_ni_fq *fq, + struct dpaa2_fd *fd) +{ + struct dpaa2_ni_softc *sc = device_get_softc(chan->ni_dev); + struct dpaa2_bp_softc *bpsc; + struct dpaa2_buf *buf; + struct ifnet *ifp = sc->ifp; + struct mbuf *m; + device_t bp_dev; + bus_addr_t paddr = (bus_addr_t) fd->addr; + bus_addr_t released[DPAA2_SWP_BUFS_PER_CMD]; + void *buf_data; + int buf_idx, buf_len; + int error, released_n = 0; + + /* + * Get buffer index from the ADDR_TOK (not used by QBMan) bits of the + * physical address. + */ + buf_idx = dpaa2_ni_fd_buf_idx(fd); + buf = &sc->buf[buf_idx]; + + KASSERT(buf->type == DPAA2_BUF_RX, ("%s: not Rx buffer", __func__)); + if (paddr != buf->rx.paddr) { + panic("%s: unexpected physical address: fd(%#jx) != buf(%#jx)", + __func__, paddr, buf->rx.paddr); + } + + /* Update statistics. */ + switch (dpaa2_ni_fd_err(fd)) { + case 1: /* Enqueue rejected by QMan */ + sc->rx_enq_rej_frames++; + break; + case 2: /* QMan IEOI error */ + sc->rx_ieoi_err_frames++; + break; + default: + break; + } + switch (dpaa2_ni_fd_format(fd)) { + case DPAA2_FD_SINGLE: + sc->rx_single_buf_frames++; + break; + case DPAA2_FD_SG: + sc->rx_sg_buf_frames++; + break; + default: + break; + } + + m = buf->rx.m; + buf->rx.m = NULL; + bus_dmamap_sync(buf->rx.dmat, buf->rx.dmap, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(buf->rx.dmat, buf->rx.dmap); + + buf_len = dpaa2_ni_fd_data_len(fd); + buf_data = (uint8_t *)buf->rx.vaddr + dpaa2_ni_fd_offset(fd); + + /* Prefetch mbuf data. */ + __builtin_prefetch(buf_data); + + /* Write value to mbuf (avoid reading). */ + m->m_flags |= M_PKTHDR; + m->m_data = buf_data; + m->m_len = buf_len; + m->m_pkthdr.len = buf_len; + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.flowid = fq->fqid; + M_HASHTYPE_SET(m, M_HASHTYPE_OPAQUE); + + (*ifp->if_input)(ifp, m); + + /* Keep the buffer to be recycled. */ + chan->recycled[chan->recycled_n++] = paddr; + KASSERT(chan->recycled_n <= DPAA2_SWP_BUFS_PER_CMD, + ("%s: too many buffers to recycle", __func__)); + + /* Re-seed and release recycled buffers back to the pool. */ + if (chan->recycled_n == DPAA2_SWP_BUFS_PER_CMD) { + /* Release new buffers to the pool if needed. */ + taskqueue_enqueue(sc->bp_taskq, &sc->bp_task); + + for (int i = 0; i < chan->recycled_n; i++) { + paddr = chan->recycled[i]; + + /* Parse ADDR_TOK of the recycled buffer. */ + buf_idx = (paddr >> DPAA2_NI_BUF_IDX_SHIFT) + & DPAA2_NI_BUF_IDX_MASK; + buf = &sc->buf[buf_idx]; + + /* Seed recycled buffer. */ + error = dpaa2_ni_seed_rxbuf(sc, buf, buf_idx); + KASSERT(error == 0, ("%s: failed to seed recycled " + "buffer: error=%d", __func__, error)); + if (__predict_false(error != 0)) { + device_printf(sc->dev, "%s: failed to seed " + "recycled buffer: error=%d\n", __func__, + error); + continue; + } + + /* Prepare buffer to be released in a single command. */ + released[released_n++] = buf->rx.paddr; + } + + /* There's only one buffer pool for now. */ + bp_dev = (device_t) rman_get_start(sc->res[BP_RID(0)]); + bpsc = device_get_softc(bp_dev); + + error = DPAA2_SWP_RELEASE_BUFS(chan->io_dev, bpsc->attr.bpid, + released, released_n); + if (__predict_false(error != 0)) { + device_printf(sc->dev, "%s: failed to release buffers " + "to the pool: error=%d\n", __func__, error); + return (error); + } + + /* Be ready to recycle the next portion of the buffers. */ + chan->recycled_n = 0; + } + + return (0); +} + +/** + * @brief Receive Rx error frames. + */ +static int +dpaa2_ni_rx_err(struct dpaa2_ni_channel *chan, struct dpaa2_ni_fq *fq, + struct dpaa2_fd *fd) +{ + device_t bp_dev; + struct dpaa2_ni_softc *sc = device_get_softc(chan->ni_dev); + struct dpaa2_bp_softc *bpsc; + struct dpaa2_buf *buf; + bus_addr_t paddr = (bus_addr_t) fd->addr; + int buf_idx, error; + + /* + * Get buffer index from the ADDR_TOK (not used by QBMan) bits of the + * physical address. + */ + buf_idx = dpaa2_ni_fd_buf_idx(fd); + buf = &sc->buf[buf_idx]; + + KASSERT(buf->type == DPAA2_BUF_RX, ("%s: not Rx buffer", __func__)); + if (paddr != buf->rx.paddr) { + panic("%s: unexpected physical address: fd(%#jx) != buf(%#jx)", + __func__, paddr, buf->rx.paddr); + } + + /* There's only one buffer pool for now. */ + bp_dev = (device_t) rman_get_start(sc->res[BP_RID(0)]); + bpsc = device_get_softc(bp_dev); + + /* Release buffer to QBMan buffer pool. */ + error = DPAA2_SWP_RELEASE_BUFS(chan->io_dev, bpsc->attr.bpid, &paddr, 1); + if (error != 0) { + device_printf(sc->dev, "%s: failed to release frame buffer to " + "the pool: error=%d\n", __func__, error); + return (error); + } + + return (0); +} + +/** + * @brief Receive Tx confirmation frames. + */ +static int +dpaa2_ni_tx_conf(struct dpaa2_ni_channel *chan, struct dpaa2_ni_fq *fq, + struct dpaa2_fd *fd) +{ + struct dpaa2_ni_softc *sc = device_get_softc(chan->ni_dev); + struct dpaa2_ni_channel *buf_chan; + struct dpaa2_ni_tx_ring *tx; + struct dpaa2_buf *buf; + bus_addr_t paddr = (bus_addr_t) (fd->addr & BUF_MAXADDR_49BIT); + uint64_t buf_idx; + int chan_idx, tx_idx; + + /* + * Get channel, Tx ring and buffer indexes from the ADDR_TOK bits + * (not used by QBMan) of the physical address. + */ + chan_idx = dpaa2_ni_fd_chan_idx(fd); + tx_idx = dpaa2_ni_fd_tx_idx(fd); + buf_idx = (uint64_t) dpaa2_ni_fd_txbuf_idx(fd); + + KASSERT(tx_idx < DPAA2_NI_MAX_TCS, ("%s: invalid Tx ring index", + __func__)); + KASSERT(buf_idx < DPAA2_NI_BUFS_PER_TX, ("%s: invalid Tx buffer index", + __func__)); + + buf_chan = sc->channels[chan_idx]; + tx = &buf_chan->txc_queue.tx_rings[tx_idx]; + buf = &tx->buf[buf_idx]; + + KASSERT(buf->type == DPAA2_BUF_TX, ("%s: not Tx buffer", __func__)); + if (paddr != buf->tx.paddr) { + panic("%s: unexpected physical address: fd(%#jx) != buf(%#jx)", + __func__, paddr, buf->tx.paddr); + } + + + bus_dmamap_unload(buf->tx.dmat, buf->tx.dmap); + if (buf->tx.sgt_paddr != 0) + bus_dmamap_unload(buf->tx.sgt_dmat, buf->tx.sgt_dmap); + m_freem(buf->tx.m); + + /* Return Tx buffer index back to the ring. */ + buf_ring_enqueue(tx->idx_br, (void *) buf_idx); + + return (0); +} + +/** + * @brief Compare versions of the DPAA2 network interface API. + */ +static int +dpaa2_ni_cmp_api_version(struct dpaa2_ni_softc *sc, uint16_t major, + uint16_t minor) +{ + if (sc->api_major == major) + return sc->api_minor - minor; + return sc->api_major - major; +} + +/** + * @brief Allocate Rx buffers visible to QBMan and release them to the pool. + */ +static int +dpaa2_ni_seed_buf_pool(struct dpaa2_ni_softc *sc, uint32_t seedn) +{ + device_t bp_dev; + struct dpaa2_bp_softc *bpsc; + struct dpaa2_buf *buf; + bus_addr_t paddr[DPAA2_SWP_BUFS_PER_CMD]; + const int allocated = DPAA2_ATOMIC_READ(&sc->buf_num); + int i, error, bufn = 0; + + KASSERT(sc->bp_dmat != NULL, ("%s: DMA tag for buffer pool not " + "created?", __func__)); + + /* There's only one buffer pool for now. */ + bp_dev = (device_t) rman_get_start(sc->res[BP_RID(0)]); + bpsc = device_get_softc(bp_dev); + + /* Limit # of buffers released to the pool. */ + if (allocated + seedn > DPAA2_NI_BUFS_MAX) + seedn = DPAA2_NI_BUFS_MAX - allocated; + + /* Release "seedn" buffers to the pool. */ + for (i = allocated; i < (allocated + seedn); i++) { + /* Enough buffers were allocated for a single command. */ + if (bufn == DPAA2_SWP_BUFS_PER_CMD) { + error = DPAA2_SWP_RELEASE_BUFS(sc->channels[0]->io_dev, + bpsc->attr.bpid, paddr, bufn); + if (error) { + device_printf(sc->dev, "%s: failed to release " + "buffers to the pool (1)\n", __func__); + return (error); + } + DPAA2_ATOMIC_ADD(&sc->buf_num, bufn); + bufn = 0; + } + + buf = &sc->buf[i]; + buf->type = DPAA2_BUF_RX; + buf->rx.m = NULL; + buf->rx.dmap = NULL; + buf->rx.paddr = 0; + buf->rx.vaddr = NULL; + error = dpaa2_ni_seed_rxbuf(sc, buf, i); + if (error) + break; + paddr[bufn] = buf->rx.paddr; + bufn++; + } + + /* Release if there are buffers left. */ + if (bufn > 0) { + error = DPAA2_SWP_RELEASE_BUFS(sc->channels[0]->io_dev, + bpsc->attr.bpid, paddr, bufn); + if (error) { + device_printf(sc->dev, "%s: failed to release " + "buffers to the pool (2)\n", __func__); + return (error); + } + DPAA2_ATOMIC_ADD(&sc->buf_num, bufn); + } + + return (0); +} + +/** + * @brief Prepare Rx buffer to be released to the buffer pool. + */ +static int +dpaa2_ni_seed_rxbuf(struct dpaa2_ni_softc *sc, struct dpaa2_buf *buf, int idx) +{ + struct mbuf *m; + bus_dmamap_t dmap; + bus_dma_segment_t segs; + int error, nsegs; + + KASSERT(sc->bp_dmat != NULL, ("%s: Buffer pool DMA tag is not " + "allocated?", __func__)); + KASSERT(buf->type == DPAA2_BUF_RX, ("%s: not Rx buffer", __func__)); + + /* Keep DMA tag for this buffer. */ + if (__predict_false(buf->rx.dmat == NULL)) + buf->rx.dmat = sc->bp_dmat; + + /* Create a DMA map for the giving buffer if it doesn't exist yet. */ + if (__predict_false(buf->rx.dmap == NULL)) { + error = bus_dmamap_create(buf->rx.dmat, 0, &dmap); + if (error) { + device_printf(sc->dev, "%s: failed to create DMA map " + "for buffer: buf_idx=%d, error=%d\n", __func__, + idx, error); + return (error); + } + buf->rx.dmap = dmap; + } + + /* Allocate mbuf if needed. */ + if (__predict_false(buf->rx.m == NULL)) { + m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, BUF_SIZE); + if (__predict_false(m == NULL)) { + device_printf(sc->dev, "%s: failed to allocate mbuf for " + "buffer\n", __func__); + return (ENOMEM); + } + m->m_len = m->m_ext.ext_size; + m->m_pkthdr.len = m->m_ext.ext_size; + buf->rx.m = m; + } else + m = buf->rx.m; + + error = bus_dmamap_load_mbuf_sg(buf->rx.dmat, buf->rx.dmap, + m, &segs, &nsegs, BUS_DMA_NOWAIT); + KASSERT(nsegs == 1, ("one segment expected: nsegs=%d", nsegs)); + KASSERT(error == 0, ("failed to map mbuf: error=%d", error)); + if (__predict_false(error != 0 || nsegs != 1)) { + device_printf(sc->dev, "%s: failed to map mbuf: error=%d, " + "nsegs=%d\n", __func__, error, nsegs); + bus_dmamap_unload(buf->rx.dmat, buf->rx.dmap); + m_freem(m); + return (error); + } + buf->rx.paddr = segs.ds_addr; + buf->rx.vaddr = m->m_data; + + /* + * Write buffer index to the ADDR_TOK (bits 63-49) which is not used by + * QBMan and is supposed to assist in physical to virtual address + * translation. + * + * NOTE: "lowaddr" and "highaddr" of the window which cannot be accessed + * by QBMan must be configured in the DMA tag accordingly. + */ + buf->rx.paddr = + ((uint64_t)(idx & DPAA2_NI_BUF_IDX_MASK) << + DPAA2_NI_BUF_IDX_SHIFT) | + (buf->rx.paddr & DPAA2_NI_BUF_ADDR_MASK); + + return (0); +} + +/** + * @brief Prepare Tx buffer to be added to the Tx ring. + */ +static int +dpaa2_ni_seed_txbuf(struct dpaa2_ni_softc *sc, struct dpaa2_buf *buf, int idx) +{ + bus_dmamap_t dmap; + int error; + + KASSERT(sc->tx_dmat != NULL, ("%s: Tx DMA tag is not allocated?", + __func__)); + KASSERT(sc->sgt_dmat != NULL, ("%s: S/G DMA tag not allocated?", + __func__)); + KASSERT(buf->type == DPAA2_BUF_TX, ("%s: not Tx buffer", __func__)); + + /* Keep DMA tags for this buffer. */ + if (__predict_true(buf->tx.dmat == NULL)) + buf->tx.dmat = sc->tx_dmat; + if (__predict_true(buf->tx.sgt_dmat == NULL)) + buf->tx.sgt_dmat = sc->sgt_dmat; + + /* Create a DMA map for the giving buffer if it doesn't exist yet. */ + if (__predict_true(buf->tx.dmap == NULL)) { + error = bus_dmamap_create(buf->tx.dmat, 0, &dmap); + if (error != 0) { + device_printf(sc->dev, "%s: failed to create " + "Tx DMA map: error=%d\n", __func__, error); + return (error); + } + buf->tx.dmap = dmap; + } + + /* Allocate a buffer to store scatter/gather table. */ + if (__predict_true(buf->tx.sgt_vaddr == NULL)) { + error = bus_dmamem_alloc(buf->tx.sgt_dmat, + &buf->tx.sgt_vaddr, BUS_DMA_ZERO | BUS_DMA_COHERENT, + &buf->tx.sgt_dmap); + if (error != 0) { + device_printf(sc->dev, "%s: failed to allocate " + "S/G table: error=%d\n", __func__, error); + return (error); + } + } + + return (0); +} + +/** + * @brief Allocate channel storage visible to QBMan. + */ +static int +dpaa2_ni_seed_chan_storage(struct dpaa2_ni_softc *sc, + struct dpaa2_ni_channel *chan) +{ + struct dpaa2_buf *buf = &chan->store; + int error; + + KASSERT(sc->st_dmat != NULL, ("%s: channel storage DMA tag is not " + "allocated?", __func__)); + KASSERT(buf->type == DPAA2_BUF_STORE, ("%s: not channel storage buffer", + __func__)); + + /* Keep DMA tag for this buffer. */ + if (__predict_false(buf->store.dmat == NULL)) { + buf->store.dmat = sc->st_dmat; + } + + if (__predict_false(buf->store.vaddr == NULL)) { + error = bus_dmamem_alloc(buf->store.dmat, &buf->store.vaddr, + BUS_DMA_ZERO | BUS_DMA_COHERENT, &buf->store.dmap); + if (error) { + device_printf(sc->dev, "%s: failed to allocate channel " + "storage\n", __func__); + return (error); + } + } + + if (__predict_false(buf->store.paddr == 0)) { + error = bus_dmamap_load(buf->store.dmat, buf->store.dmap, + buf->store.vaddr, ETH_STORE_SIZE, dpaa2_ni_dmamap_cb, + &buf->store.paddr, BUS_DMA_NOWAIT); + if (error) { + device_printf(sc->dev, "%s: failed to map channel " + "storage\n", __func__); + return (error); + } + } + + chan->store_sz = ETH_STORE_FRAMES; + chan->store_idx = 0; + + return (0); +} + +/** + * @brief Build a DPAA2 frame descriptor. + */ +static int +dpaa2_ni_build_fd(struct dpaa2_ni_softc *sc, struct dpaa2_ni_tx_ring *tx, + struct dpaa2_buf *buf, bus_dma_segment_t *txsegs, int txnsegs, + struct dpaa2_fd *fd) +{ + struct dpaa2_ni_channel *chan = tx->fq->chan; + struct dpaa2_sg_entry *sgt; + int i, error; + + KASSERT(txnsegs <= DPAA2_TX_SEGLIMIT, ("%s: too many segments, " + "txnsegs (%d) > %d", __func__, txnsegs, DPAA2_TX_SEGLIMIT)); + KASSERT(buf->type == DPAA2_BUF_TX, ("%s: not Tx buffer", __func__)); + KASSERT(buf->tx.sgt_vaddr != NULL, ("%s: S/G table not allocated?", + __func__)); + + /* Reset frame descriptor fields. */ + memset(fd, 0, sizeof(*fd)); + + if (__predict_true(txnsegs <= DPAA2_TX_SEGLIMIT)) { + /* Populate S/G table. */ + sgt = (struct dpaa2_sg_entry *) buf->tx.sgt_vaddr + + sc->tx_data_off; + for (i = 0; i < txnsegs; i++) { + sgt[i].addr = (uint64_t) txsegs[i].ds_addr; + sgt[i].len = (uint32_t) txsegs[i].ds_len; + sgt[i].offset_fmt = 0u; + } + sgt[i-1].offset_fmt |= 0x8000u; /* set final entry flag */ + + KASSERT(buf->tx.sgt_paddr == 0, ("%s: sgt_paddr(%#jx) != 0", + __func__, buf->tx.sgt_paddr)); + + /* Load S/G table. */ + error = bus_dmamap_load(buf->tx.sgt_dmat, buf->tx.sgt_dmap, + buf->tx.sgt_vaddr, DPAA2_TX_SGT_SZ, dpaa2_ni_dmamap_cb, + &buf->tx.sgt_paddr, BUS_DMA_NOWAIT); + if (__predict_false(error != 0)) { + device_printf(sc->dev, "%s: failed to map S/G table: " + "error=%d\n", __func__, error); + return (error); + } + buf->tx.paddr = buf->tx.sgt_paddr; + buf->tx.vaddr = buf->tx.sgt_vaddr; + sc->tx_sg_frames++; /* for sysctl(9) */ + } else { + return (EINVAL); + } + + fd->addr = + ((uint64_t)(chan->flowid & DPAA2_NI_BUF_CHAN_MASK) << + DPAA2_NI_BUF_CHAN_SHIFT) | + ((uint64_t)(tx->txid & DPAA2_NI_TX_IDX_MASK) << + DPAA2_NI_TX_IDX_SHIFT) | + ((uint64_t)(buf->tx.idx & DPAA2_NI_TXBUF_IDX_MASK) << + DPAA2_NI_TXBUF_IDX_SHIFT) | + (buf->tx.paddr & DPAA2_NI_BUF_ADDR_MASK); + + fd->data_length = (uint32_t) buf->tx.m->m_pkthdr.len; + fd->bpid_ivp_bmt = 0; + fd->offset_fmt_sl = 0x2000u | sc->tx_data_off; + fd->ctrl = 0x00800000u; + + return (0); +} + +static int +dpaa2_ni_fd_err(struct dpaa2_fd *fd) +{ + return ((fd->ctrl >> DPAA2_NI_FD_ERR_SHIFT) & DPAA2_NI_FD_ERR_MASK); +} + +static uint32_t +dpaa2_ni_fd_data_len(struct dpaa2_fd *fd) +{ + if (dpaa2_ni_fd_short_len(fd)) + return (fd->data_length & DPAA2_NI_FD_LEN_MASK); + + return (fd->data_length); +} + +static int +dpaa2_ni_fd_chan_idx(struct dpaa2_fd *fd) +{ + return ((((bus_addr_t) fd->addr) >> DPAA2_NI_BUF_CHAN_SHIFT) & + DPAA2_NI_BUF_CHAN_MASK); +} + +static int +dpaa2_ni_fd_buf_idx(struct dpaa2_fd *fd) +{ + return ((((bus_addr_t) fd->addr) >> DPAA2_NI_BUF_IDX_SHIFT) & + DPAA2_NI_BUF_IDX_MASK); +} + +static int +dpaa2_ni_fd_tx_idx(struct dpaa2_fd *fd) +{ + return ((((bus_addr_t) fd->addr) >> DPAA2_NI_TX_IDX_SHIFT) & + DPAA2_NI_TX_IDX_MASK); +} + +static int +dpaa2_ni_fd_txbuf_idx(struct dpaa2_fd *fd) +{ + return ((((bus_addr_t) fd->addr) >> DPAA2_NI_TXBUF_IDX_SHIFT) & + DPAA2_NI_TXBUF_IDX_MASK); +} + +static int +dpaa2_ni_fd_format(struct dpaa2_fd *fd) +{ + return ((enum dpaa2_fd_format)((fd->offset_fmt_sl >> + DPAA2_NI_FD_FMT_SHIFT) & DPAA2_NI_FD_FMT_MASK)); +} + +static bool +dpaa2_ni_fd_short_len(struct dpaa2_fd *fd) +{ + return (((fd->offset_fmt_sl >> DPAA2_NI_FD_SL_SHIFT) + & DPAA2_NI_FD_SL_MASK) == 1); +} + +static int +dpaa2_ni_fd_offset(struct dpaa2_fd *fd) +{ + return (fd->offset_fmt_sl & DPAA2_NI_FD_OFFSET_MASK); +} + +/** + * @brief Collect statistics of the network interface. + */ +static int +dpaa2_ni_collect_stats(SYSCTL_HANDLER_ARGS) +{ + struct dpaa2_ni_softc *sc = (struct dpaa2_ni_softc *) arg1; + struct dpni_stat *stat = &dpni_stat_sysctls[oidp->oid_number]; + device_t child = sc->dev; + uint64_t cnt[DPAA2_NI_STAT_COUNTERS]; + uint64_t result = 0; + int error; + + error = DPAA2_CMD_NI_GET_STATISTICS(sc->dev, child, + dpaa2_mcp_tk(sc->cmd, sc->ni_token), stat->page, 0, cnt); + if (!error) + result = cnt[stat->cnt]; + + return (sysctl_handle_64(oidp, &result, 0, req)); +} + +static int +dpaa2_ni_collect_buf_num(SYSCTL_HANDLER_ARGS) +{ + struct dpaa2_ni_softc *sc = (struct dpaa2_ni_softc *) arg1; + uint32_t buf_num = DPAA2_ATOMIC_READ(&sc->buf_num); + + return (sysctl_handle_32(oidp, &buf_num, 0, req)); +} + +static int +dpaa2_ni_collect_buf_free(SYSCTL_HANDLER_ARGS) +{ + struct dpaa2_ni_softc *sc = (struct dpaa2_ni_softc *) arg1; + uint32_t buf_free = DPAA2_ATOMIC_READ(&sc->buf_free); + + return (sysctl_handle_32(oidp, &buf_free, 0, req)); +} + +static int +dpaa2_ni_set_hash(device_t dev, uint64_t flags) +{ + struct dpaa2_ni_softc *sc = device_get_softc(dev); + uint64_t key = 0; + int i; + + if (!(sc->attr.num.queues > 1)) { + return (EOPNOTSUPP); + } + + for (i = 0; i < ARRAY_SIZE(dist_fields); i++) { + if (dist_fields[i].rxnfc_field & flags) { + key |= dist_fields[i].id; + } + } + + return (dpaa2_ni_set_dist_key(dev, DPAA2_NI_DIST_MODE_HASH, key)); +} + +/** + * @brief Set Rx distribution (hash or flow classification) key flags is a + * combination of RXH_ bits. + */ +static int +dpaa2_ni_set_dist_key(device_t dev, enum dpaa2_ni_dist_mode type, uint64_t flags) +{ + device_t child = dev; + struct dpaa2_ni_softc *sc = device_get_softc(dev); + struct dpkg_profile_cfg cls_cfg; + struct dpkg_extract *key; + struct dpaa2_buf *buf = &sc->rxd_kcfg; + int i, error = 0; + + KASSERT(buf->type == DPAA2_BUF_STORE, ("%s: not storage buffer", + __func__)); + if (__predict_true(buf->store.dmat == NULL)) + buf->store.dmat = sc->rxd_dmat; + + memset(&cls_cfg, 0, sizeof(cls_cfg)); + + /* Configure extracts according to the given flags. */ + for (i = 0; i < ARRAY_SIZE(dist_fields); i++) { + key = &cls_cfg.extracts[cls_cfg.num_extracts]; + + if (!(flags & dist_fields[i].id)) + continue; + + if (cls_cfg.num_extracts >= DPKG_MAX_NUM_OF_EXTRACTS) { + device_printf(dev, "%s: failed to add key extraction " + "rule\n", __func__); + return (E2BIG); + } + + key->type = DPKG_EXTRACT_FROM_HDR; + key->extract.from_hdr.prot = dist_fields[i].cls_prot; + key->extract.from_hdr.type = DPKG_FULL_FIELD; + key->extract.from_hdr.field = dist_fields[i].cls_field; + cls_cfg.num_extracts++; + } + + error = bus_dmamem_alloc(buf->store.dmat, &buf->store.vaddr, + BUS_DMA_ZERO | BUS_DMA_COHERENT, &buf->store.dmap); + if (error != 0) { + device_printf(dev, "%s: failed to allocate a buffer for Rx " + "traffic distribution key configuration\n", __func__); + return (error); + } + + error = dpaa2_ni_prepare_key_cfg(&cls_cfg, (uint8_t *) buf->store.vaddr); + if (error != 0) { + device_printf(dev, "%s: failed to prepare key configuration: " + "error=%d\n", __func__, error); + return (error); + } + + /* Prepare for setting the Rx dist. */ + error = bus_dmamap_load(buf->store.dmat, buf->store.dmap, + buf->store.vaddr, DPAA2_CLASSIFIER_DMA_SIZE, dpaa2_ni_dmamap_cb, + &buf->store.paddr, BUS_DMA_NOWAIT); + if (error != 0) { + device_printf(sc->dev, "%s: failed to map a buffer for Rx " + "traffic distribution key configuration\n", __func__); + return (error); + } + + if (type == DPAA2_NI_DIST_MODE_HASH) { + error = DPAA2_CMD_NI_SET_RX_TC_DIST(dev, child, dpaa2_mcp_tk( + sc->cmd, sc->ni_token), sc->attr.num.queues, 0, + DPAA2_NI_DIST_MODE_HASH, buf->store.paddr); + if (error != 0) + device_printf(dev, "%s: failed to set distribution mode " + "and size for the traffic class\n", __func__); + } + + return (error); +} + +/** + * @brief Prepares extract parameters. + * + * cfg: Defining a full Key Generation profile. + * key_cfg_buf: Zeroed 256 bytes of memory before mapping it to DMA. + */ +static int +dpaa2_ni_prepare_key_cfg(struct dpkg_profile_cfg *cfg, uint8_t *key_cfg_buf) +{ + struct dpni_ext_set_rx_tc_dist *dpni_ext; + struct dpni_dist_extract *extr; + int i, j; + + if (cfg->num_extracts > DPKG_MAX_NUM_OF_EXTRACTS) + return (EINVAL); + + dpni_ext = (struct dpni_ext_set_rx_tc_dist *) key_cfg_buf; + dpni_ext->num_extracts = cfg->num_extracts; + + for (i = 0; i < cfg->num_extracts; i++) { + extr = &dpni_ext->extracts[i]; + + switch (cfg->extracts[i].type) { + case DPKG_EXTRACT_FROM_HDR: + extr->prot = cfg->extracts[i].extract.from_hdr.prot; + extr->efh_type = + cfg->extracts[i].extract.from_hdr.type & 0x0Fu; + extr->size = cfg->extracts[i].extract.from_hdr.size; + extr->offset = cfg->extracts[i].extract.from_hdr.offset; + extr->field = cfg->extracts[i].extract.from_hdr.field; + extr->hdr_index = + cfg->extracts[i].extract.from_hdr.hdr_index; + break; + case DPKG_EXTRACT_FROM_DATA: + extr->size = cfg->extracts[i].extract.from_data.size; + extr->offset = + cfg->extracts[i].extract.from_data.offset; + break; + case DPKG_EXTRACT_FROM_PARSE: + extr->size = cfg->extracts[i].extract.from_parse.size; + extr->offset = + cfg->extracts[i].extract.from_parse.offset; + break; + default: + return (EINVAL); + } + + extr->num_of_byte_masks = cfg->extracts[i].num_of_byte_masks; + extr->extract_type = cfg->extracts[i].type & 0x0Fu; + + for (j = 0; j < DPKG_NUM_OF_MASKS; j++) { + extr->masks[j].mask = cfg->extracts[i].masks[j].mask; + extr->masks[j].offset = + cfg->extracts[i].masks[j].offset; + } + } + + return (0); +} + +/** + * @brief Obtain the next dequeue response from the channel storage. + */ +static int +dpaa2_ni_chan_storage_next(struct dpaa2_ni_channel *chan, struct dpaa2_dq **dq) +{ + struct dpaa2_buf *buf = &chan->store; + struct dpaa2_dq *msgs = buf->store.vaddr; + struct dpaa2_dq *msg = &msgs[chan->store_idx]; + int rc = EINPROGRESS; + + chan->store_idx++; + + if (msg->fdr.desc.stat & DPAA2_DQ_STAT_EXPIRED) { + rc = EALREADY; /* VDQ command is expired */ + chan->store_idx = 0; + if (!(msg->fdr.desc.stat & DPAA2_DQ_STAT_VALIDFRAME)) + msg = NULL; /* Null response, FD is invalid */ + } + if (msg->fdr.desc.stat & DPAA2_DQ_STAT_FQEMPTY) { + rc = ENOENT; /* FQ is empty */ + chan->store_idx = 0; + } + + if (dq != NULL) + *dq = msg; + + return (rc); +} + +static device_method_t dpaa2_ni_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, dpaa2_ni_probe), + DEVMETHOD(device_attach, dpaa2_ni_attach), + DEVMETHOD(device_detach, dpaa2_ni_detach), + + /* mii via memac_mdio */ + DEVMETHOD(miibus_statchg, dpaa2_ni_miibus_statchg), + + DEVMETHOD_END +}; + +static driver_t dpaa2_ni_driver = { + "dpaa2_ni", + dpaa2_ni_methods, + sizeof(struct dpaa2_ni_softc), +}; + +DRIVER_MODULE(miibus, dpaa2_ni, miibus_driver, 0, 0); +DRIVER_MODULE(dpaa2_ni, dpaa2_rc, dpaa2_ni_driver, 0, 0); + +MODULE_DEPEND(dpaa2_ni, miibus, 1, 1, 1); +#ifdef DEV_ACPI +MODULE_DEPEND(dpaa2_ni, memac_mdio_acpi, 1, 1, 1); +#endif +#ifdef FDT +MODULE_DEPEND(dpaa2_ni, memac_mdio_fdt, 1, 1, 1); +#endif diff --git a/sys/dev/dpaa2/dpaa2_ni.h b/sys/dev/dpaa2/dpaa2_ni.h new file mode 100644 index 000000000000..929a4d0d4966 --- /dev/null +++ b/sys/dev/dpaa2/dpaa2_ni.h @@ -0,0 +1,607 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright © 2021-2022 Dmitry Salychev + * Copyright © 2022 Mathew McBride + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _DPAA2_NI_H +#define _DPAA2_NI_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "dpaa2_types.h" +#include "dpaa2_mcp.h" +#include "dpaa2_swp.h" +#include "dpaa2_io.h" +#include "dpaa2_mac.h" +#include "dpaa2_ni_dpkg.h" + +/* Name of the DPAA2 network interface. */ +#define DPAA2_NI_IFNAME "dpni" + +/* Maximum resources per DPNI: 16 DPIOs + 16 DPCONs + 1 DPBP + 1 DPMCP. */ +#define DPAA2_NI_MAX_RESOURCES 34 + +#define DPAA2_NI_MSI_COUNT 1 /* MSIs per DPNI */ +#define DPAA2_NI_MAX_CHANNELS 16 /* to distribute ingress traffic to cores */ +#define DPAA2_NI_MAX_TCS 8 /* traffic classes per DPNI */ +#define DPAA2_NI_MAX_POOLS 8 /* buffer pools per DPNI */ + +/* Maximum number of Rx buffers. */ +#define DPAA2_NI_BUFS_INIT (50u * DPAA2_SWP_BUFS_PER_CMD) +#define DPAA2_NI_BUFS_MAX (1 << 15) /* 15 bits for buffer index max. */ + +/* Maximum number of buffers allocated per Tx ring. */ +#define DPAA2_NI_BUFS_PER_TX (1 << 7) +#define DPAA2_NI_MAX_BPTX (1 << 8) /* 8 bits for buffer index max. */ + +/* Number of the DPNI statistics counters. */ +#define DPAA2_NI_STAT_COUNTERS 7u +#define DPAA2_NI_STAT_SYSCTLS 9u + +/* Error and status bits in the frame annotation status word. */ +#define DPAA2_NI_FAS_DISC 0x80000000 /* debug frame */ +#define DPAA2_NI_FAS_MS 0x40000000 /* MACSEC frame */ +#define DPAA2_NI_FAS_PTP 0x08000000 +#define DPAA2_NI_FAS_MC 0x04000000 /* Ethernet multicast frame */ +#define DPAA2_NI_FAS_BC 0x02000000 /* Ethernet broadcast frame */ +#define DPAA2_NI_FAS_KSE 0x00040000 +#define DPAA2_NI_FAS_EOFHE 0x00020000 +#define DPAA2_NI_FAS_MNLE 0x00010000 +#define DPAA2_NI_FAS_TIDE 0x00008000 +#define DPAA2_NI_FAS_PIEE 0x00004000 +#define DPAA2_NI_FAS_FLE 0x00002000 /* Frame length error */ +#define DPAA2_NI_FAS_FPE 0x00001000 /* Frame physical error */ +#define DPAA2_NI_FAS_PTE 0x00000080 +#define DPAA2_NI_FAS_ISP 0x00000040 +#define DPAA2_NI_FAS_PHE 0x00000020 +#define DPAA2_NI_FAS_BLE 0x00000010 +#define DPAA2_NI_FAS_L3CV 0x00000008 /* L3 csum validation performed */ +#define DPAA2_NI_FAS_L3CE 0x00000004 /* L3 csum error */ +#define DPAA2_NI_FAS_L4CV 0x00000002 /* L4 csum validation performed */ +#define DPAA2_NI_FAS_L4CE 0x00000001 /* L4 csum error */ + +/* Mask for errors on the ingress path. */ +#define DPAA2_NI_FAS_RX_ERR_MASK (DPAA2_NI_FAS_KSE | \ + DPAA2_NI_FAS_EOFHE | \ + DPAA2_NI_FAS_MNLE | \ + DPAA2_NI_FAS_TIDE | \ + DPAA2_NI_FAS_PIEE | \ + DPAA2_NI_FAS_FLE | \ + DPAA2_NI_FAS_FPE | \ + DPAA2_NI_FAS_PTE | \ + DPAA2_NI_FAS_ISP | \ + DPAA2_NI_FAS_PHE | \ + DPAA2_NI_FAS_BLE | \ + DPAA2_NI_FAS_L3CE | \ + DPAA2_NI_FAS_L4CE \ +) + +/* Option bits to select specific queue configuration options to apply. */ +#define DPAA2_NI_QUEUE_OPT_USER_CTX 0x00000001 +#define DPAA2_NI_QUEUE_OPT_DEST 0x00000002 +#define DPAA2_NI_QUEUE_OPT_FLC 0x00000004 +#define DPAA2_NI_QUEUE_OPT_HOLD_ACTIVE 0x00000008 +#define DPAA2_NI_QUEUE_OPT_SET_CGID 0x00000040 +#define DPAA2_NI_QUEUE_OPT_CLEAR_CGID 0x00000080 + +/* DPNI link configuration options. */ +#define DPAA2_NI_LINK_OPT_AUTONEG ((uint64_t) 0x01u) +#define DPAA2_NI_LINK_OPT_HALF_DUPLEX ((uint64_t) 0x02u) +#define DPAA2_NI_LINK_OPT_PAUSE ((uint64_t) 0x04u) +#define DPAA2_NI_LINK_OPT_ASYM_PAUSE ((uint64_t) 0x08u) +#define DPAA2_NI_LINK_OPT_PFC_PAUSE ((uint64_t) 0x10u) + +/* + * Number of times to retry a frame enqueue before giving up. Value determined + * empirically, in order to minimize the number of frames dropped on Tx. + */ +#define DPAA2_NI_ENQUEUE_RETRIES 10 + +enum dpaa2_ni_queue_type { + DPAA2_NI_QUEUE_RX = 0, + DPAA2_NI_QUEUE_TX, + DPAA2_NI_QUEUE_TX_CONF, + DPAA2_NI_QUEUE_RX_ERR +}; + +enum dpaa2_ni_dest_type { + DPAA2_NI_DEST_NONE = 0, + DPAA2_NI_DEST_DPIO, + DPAA2_NI_DEST_DPCON +}; + +enum dpaa2_ni_ofl_type { + DPAA2_NI_OFL_RX_L3_CSUM = 0, + DPAA2_NI_OFL_RX_L4_CSUM, + DPAA2_NI_OFL_TX_L3_CSUM, + DPAA2_NI_OFL_TX_L4_CSUM, + DPAA2_NI_OFL_FLCTYPE_HASH /* FD flow context for AIOP/CTLU */ +}; + +/** + * @brief DPNI ingress traffic distribution mode. + */ +enum dpaa2_ni_dist_mode { + DPAA2_NI_DIST_MODE_NONE = 0, + DPAA2_NI_DIST_MODE_HASH, + DPAA2_NI_DIST_MODE_FS +}; + +/** + * @brief DPNI behavior in case of errors. + */ +enum dpaa2_ni_err_action { + DPAA2_NI_ERR_DISCARD = 0, + DPAA2_NI_ERR_CONTINUE, + DPAA2_NI_ERR_SEND_TO_ERROR_QUEUE +}; + +struct dpaa2_ni_channel; +struct dpaa2_ni_fq; + +/** + * @brief Attributes of the DPNI object. + * + * options: ... + * wriop_ver: Revision of the underlying WRIOP hardware block. + */ +struct dpaa2_ni_attr { + uint32_t options; + uint16_t wriop_ver; + struct { + uint16_t fs; + uint8_t mac; + uint8_t vlan; + uint8_t qos; + } entries; + struct { + uint8_t queues; + uint8_t rx_tcs; + uint8_t tx_tcs; + uint8_t channels; + uint8_t cgs; + } num; + struct { + uint8_t fs; + uint8_t qos; + } key_size; +}; + +/** + * @brief Tx ring. + * + * fq: Parent (TxConf) frame queue. + * fqid: ID of the logical Tx queue. + * mbuf_br: Ring buffer for mbufs to transmit. + * mbuf_lock: Lock for the ring buffer. + */ +struct dpaa2_ni_tx_ring { + struct dpaa2_ni_fq *fq; + uint32_t fqid; + uint32_t txid; /* Tx ring index */ + + /* Ring buffer for indexes in "buf" array. */ + struct buf_ring *idx_br; + struct mtx lock; + + /* Buffers to DMA load/unload Tx mbufs. */ + struct dpaa2_buf buf[DPAA2_NI_BUFS_PER_TX]; +}; + +/** + * @brief A Frame Queue is the basic queuing structure used by the QMan. + * + * It comprises a list of frame descriptors (FDs), so it can be thought of + * as a queue of frames. + * + * NOTE: When frames on a FQ are ready to be processed, the FQ is enqueued + * onto a work queue (WQ). + * + * fqid: Frame queue ID, can be used to enqueue/dequeue or execute other + * commands on the queue through DPIO. + * txq_n: Number of configured Tx queues. + * tx_fqid: Frame queue IDs of the Tx queues which belong to the same flowid. + * Note that Tx queues are logical queues and not all management + * commands are available on these queue types. + * qdbin: Queue destination bin. Can be used with the DPIO enqueue + * operation based on QDID, QDBIN and QPRI. Note that all Tx queues + * with the same flowid have the same destination bin. + */ +struct dpaa2_ni_fq { + int (*consume)(struct dpaa2_ni_channel *, + struct dpaa2_ni_fq *, struct dpaa2_fd *); + + struct dpaa2_ni_channel *chan; + uint32_t fqid; + uint16_t flowid; + uint8_t tc; + enum dpaa2_ni_queue_type type; + + /* Optional fields (for TxConf queue). */ + struct dpaa2_ni_tx_ring tx_rings[DPAA2_NI_MAX_TCS]; + uint32_t tx_qdbin; +} __aligned(CACHE_LINE_SIZE); + +/** + * @brief QBMan channel to process ingress traffic (Rx, Tx conf). + * + * NOTE: Several WQs are organized into a single WQ Channel. + */ +struct dpaa2_ni_channel { + device_t ni_dev; + device_t io_dev; + device_t con_dev; + uint16_t id; + uint16_t flowid; + + /* For debug purposes only! */ + uint64_t tx_frames; + uint64_t tx_dropped; + + /* Context to configure CDAN. */ + struct dpaa2_io_notif_ctx ctx; + + /* Channel storage (to keep responses from VDQ command). */ + struct dpaa2_buf store; + uint32_t store_sz; /* in frames */ + uint32_t store_idx; /* frame index */ + + /* Recycled buffers to release back to the pool. */ + uint32_t recycled_n; + bus_addr_t recycled[DPAA2_SWP_BUFS_PER_CMD]; + + /* Frame queues */ + uint32_t rxq_n; + struct dpaa2_ni_fq rx_queues[DPAA2_NI_MAX_TCS]; + struct dpaa2_ni_fq txc_queue; +}; + +/** + * @brief Configuration of the network interface queue. + * + * NOTE: This configuration is used to obtain information of a queue by + * DPNI_GET_QUEUE command and update it by DPNI_SET_QUEUE one. + * + * It includes binding of the queue to a DPIO or DPCON object to receive + * notifications and traffic on the CPU. + * + * user_ctx: (r/w) User defined data, presented along with the frames + * being dequeued from this queue. + * flow_ctx: (r/w) Set default FLC value for traffic dequeued from this queue. + * Please check description of FD structure for more information. + * Note that FLC values set using DPNI_ADD_FS_ENTRY, if any, take + * precedence over values per queue. + * dest_id: (r/w) The ID of a DPIO or DPCON object, depending on + * DEST_TYPE (in flags) value. This field is ignored for DEST_TYPE + * set to 0 (DPNI_DEST_NONE). + * fqid: (r) Frame queue ID, can be used to enqueue/dequeue or execute + * other commands on the queue through DPIO. Note that Tx queues + * are logical queues and not all management commands are available + * on these queue types. + * qdbin: (r) Queue destination bin. Can be used with the DPIO enqueue + * operation based on QDID, QDBIN and QPRI. + * type: Type of the queue to set configuration to. + * tc: Traffic class. Ignored for QUEUE_TYPE 2 and 3 (Tx confirmation + * and Rx error queues). + * idx: Selects a specific queue out of the set of queues in a TC. + * Accepted values are in range 0 to NUM_QUEUES–1. This field is + * ignored for QUEUE_TYPE 3 (Rx error queue). For access to the + * shared Tx confirmation queue (for Tx confirmation mode 1), this + * field must be set to 0xff. + * cgid: (r/w) Congestion group ID. + * chan_id: (w) Channel index to be configured. Used only when QUEUE_TYPE is + * set to DPNI_QUEUE_TX. + * priority: (r/w) Sets the priority in the destination DPCON or DPIO for + * dequeued traffic. Supported values are 0 to # of priorities in + * destination DPCON or DPIO - 1. This field is ignored for + * DEST_TYPE set to 0 (DPNI_DEST_NONE), except if this DPNI is in + * AIOP context. In that case the DPNI_SET_QUEUE can be used to + * override the default assigned priority of the FQ from the TC. + * options: Option bits selecting specific configuration options to apply. + * See DPAA2_NI_QUEUE_OPT_* for details. + * dest_type: Type of destination for dequeued traffic. + * cgid_valid: (r) Congestion group ID is valid. + * stash_control: (r/w) If true, lowest 6 bits of FLC are used for stash control. + * Please check description of FD structure for more information. + * hold_active: (r/w) If true, this flag prevents the queue from being + * rescheduled between DPIOs while it carries traffic and is active + * on one DPIO. Can help reduce reordering if one queue is services + * on multiple CPUs, but the queue is also more likely to be trapped + * in one DPIO, especially when congested. + */ +struct dpaa2_ni_queue_cfg { + uint64_t user_ctx; + uint64_t flow_ctx; + uint32_t dest_id; + uint32_t fqid; + uint16_t qdbin; + enum dpaa2_ni_queue_type type; + uint8_t tc; + uint8_t idx; + uint8_t cgid; + uint8_t chan_id; + uint8_t priority; + uint8_t options; + + enum dpaa2_ni_dest_type dest_type; + bool cgid_valid; + bool stash_control; + bool hold_active; +}; + +/** + * @brief Buffer layout attributes. + * + * pd_size: Size kept for private data (in bytes). + * fd_align: Frame data alignment. + * head_size: Data head room. + * tail_size: Data tail room. + * options: ... + * pass_timestamp: Timestamp is included in the buffer layout. + * pass_parser_result: Parsing results are included in the buffer layout. + * pass_frame_status: Frame status is included in the buffer layout. + * pass_sw_opaque: SW annotation is activated. + * queue_type: Type of a queue this configuration applies to. + */ +struct dpaa2_ni_buf_layout { + uint16_t pd_size; + uint16_t fd_align; + uint16_t head_size; + uint16_t tail_size; + uint16_t options; + bool pass_timestamp; + bool pass_parser_result; + bool pass_frame_status; + bool pass_sw_opaque; + enum dpaa2_ni_queue_type queue_type; +}; + +/** + * @brief Buffer pools configuration for a network interface. + */ +struct dpaa2_ni_pools_cfg { + uint8_t pools_num; + struct { + uint32_t bp_obj_id; + uint16_t buf_sz; + int backup_flag; /* 0 - regular pool, 1 - backup pool */ + } pools[DPAA2_NI_MAX_POOLS]; +}; + +/** + * @brief Errors behavior configuration for a network interface. + * + * err_mask: The errors mask to configure. + * action: Desired action for the errors selected in the mask. + * set_err_fas: Set to true to mark the errors in frame annotation + * status (FAS); relevant for non-discard actions only. + */ +struct dpaa2_ni_err_cfg { + uint32_t err_mask; + enum dpaa2_ni_err_action action; + bool set_err_fas; +}; + +/** + * @brief Link configuration. + * + * options: Mask of available options. + * adv_speeds: Speeds that are advertised for autoneg. + * rate: Rate in Mbps. + */ +struct dpaa2_ni_link_cfg { + uint64_t options; + uint64_t adv_speeds; + uint32_t rate; +}; + +/** + * @brief Link state. + * + * options: Mask of available options. + * adv_speeds: Speeds that are advertised for autoneg. + * sup_speeds: Speeds capability of the PHY. + * rate: Rate in Mbps. + * link_up: Link state (true if link is up, false otherwise). + * state_valid: Ignore/Update the state of the link. + */ +struct dpaa2_ni_link_state { + uint64_t options; + uint64_t adv_speeds; + uint64_t sup_speeds; + uint32_t rate; + bool link_up; + bool state_valid; +}; + +/** + * @brief QoS table configuration. + * + * kcfg_busaddr: Address of the buffer in I/O virtual address space which + * holds the QoS table key configuration. + * default_tc: Default traffic class to use in case of a lookup miss in + * the QoS table. + * discard_on_miss: Set to true to discard frames in case of no match. + * Default traffic class will be used otherwise. + * keep_entries: Set to true to keep existing QoS table entries. This + * option will work properly only for DPNI objects created + * with DPNI_OPT_HAS_KEY_MASKING option. + */ +struct dpaa2_ni_qos_table { + uint64_t kcfg_busaddr; + uint8_t default_tc; + bool discard_on_miss; + bool keep_entries; +}; + +/** + * @brief Context to add multicast physical addresses to the filter table. + * + * ifp: Network interface associated with the context. + * error: Result of the last MC command. + * nent: Number of entries added. + */ +struct dpaa2_ni_mcaddr_ctx { + struct ifnet *ifp; + int error; + int nent; +}; + +struct dpaa2_eth_dist_fields { + uint64_t rxnfc_field; + enum net_prot cls_prot; + int cls_field; + int size; + uint64_t id; +}; + +struct dpni_mask_cfg { + uint8_t mask; + uint8_t offset; +} __packed; + +struct dpni_dist_extract { + uint8_t prot; + uint8_t efh_type; /* EFH type is in the 4 LSBs. */ + uint8_t size; + uint8_t offset; + uint32_t field; + uint8_t hdr_index; + uint8_t constant; + uint8_t num_of_repeats; + uint8_t num_of_byte_masks; + uint8_t extract_type; /* Extraction type is in the 4 LSBs */ + uint8_t _reserved[3]; + struct dpni_mask_cfg masks[4]; +} __packed; + +struct dpni_ext_set_rx_tc_dist { + uint8_t num_extracts; + uint8_t _reserved[7]; + struct dpni_dist_extract extracts[DPKG_MAX_NUM_OF_EXTRACTS]; +} __packed; + +/** + * @brief Software context for the DPAA2 Network Interface driver. + */ +struct dpaa2_ni_softc { + device_t dev; + struct resource *res[DPAA2_NI_MAX_RESOURCES]; + uint16_t api_major; + uint16_t api_minor; + uint64_t rx_hash_fields; + uint16_t tx_data_off; + uint16_t tx_qdid; + uint32_t link_options; + int link_state; + + uint16_t buf_align; + uint16_t buf_sz; + + /* For debug purposes only! */ + uint64_t rx_anomaly_frames; + uint64_t rx_single_buf_frames; + uint64_t rx_sg_buf_frames; + uint64_t rx_enq_rej_frames; + uint64_t rx_ieoi_err_frames; + uint64_t tx_single_buf_frames; + uint64_t tx_sg_frames; + + /* Attributes of the DPAA2 network interface. */ + struct dpaa2_ni_attr attr; + + /* Helps to send commands to MC. */ + struct dpaa2_cmd *cmd; + uint16_t rc_token; + uint16_t ni_token; + + /* For network interface and miibus. */ + struct ifnet *ifp; + uint32_t if_flags; + struct mtx lock; + device_t miibus; + struct mii_data *mii; + boolean_t fixed_link; + struct ifmedia fixed_ifmedia; + int media_status; + + /* DMA resources */ + bus_dma_tag_t bp_dmat; /* for buffer pool */ + bus_dma_tag_t tx_dmat; /* for Tx buffers */ + bus_dma_tag_t st_dmat; /* for channel storage */ + bus_dma_tag_t rxd_dmat; /* for Rx distribution key */ + bus_dma_tag_t qos_dmat; /* for QoS table key */ + bus_dma_tag_t sgt_dmat; /* for scatter/gather tables */ + + struct dpaa2_buf qos_kcfg; /* QoS table key config. */ + struct dpaa2_buf rxd_kcfg; /* Rx distribution key config. */ + + /* Channels and RxError frame queue */ + uint32_t chan_n; + struct dpaa2_ni_channel *channels[DPAA2_NI_MAX_CHANNELS]; + struct dpaa2_ni_fq rxe_queue; /* one per network interface */ + + /* Rx buffers for buffer pool. */ + struct dpaa2_atomic buf_num; + struct dpaa2_atomic buf_free; /* for sysctl(9) only */ + struct dpaa2_buf buf[DPAA2_NI_BUFS_MAX]; + + /* Interrupts */ + int irq_rid[DPAA2_NI_MSI_COUNT]; + struct resource *irq_res; + void *intr; /* interrupt handle */ + + /* Tasks */ + struct taskqueue *bp_taskq; + struct task bp_task; + + /* Callouts */ + struct callout mii_callout; + + struct { + uint32_t dpmac_id; + uint8_t addr[ETHER_ADDR_LEN]; + device_t phy_dev; + int phy_loc; + } mac; /* Info about connected DPMAC (if exists). */ +}; + +extern struct resource_spec dpaa2_ni_spec[]; + +#endif /* _DPAA2_NI_H */ diff --git a/sys/dev/dpaa2/dpaa2_ni_dpkg.h b/sys/dev/dpaa2/dpaa2_ni_dpkg.h new file mode 100644 index 000000000000..209486a1ce98 --- /dev/null +++ b/sys/dev/dpaa2/dpaa2_ni_dpkg.h @@ -0,0 +1,536 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause AND BSD-2-Clause + * + * Copyright © 2013-2015 Freescale Semiconductor, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Original source file obtained from: + * drivers/net/ethernet/freescale/dpaa2/dpkg.h + * + * Commit: 4c86114194e644b6da9107d75910635c9e87179e + * Repository: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git + */ + +/* + * Copyright © 2021-2022 Dmitry Salychev + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _DPAA2_NI_DPKG_H +#define _DPAA2_NI_DPKG_H + +#define BIT(x) (1ul << (x)) + +/** + * DPKG_NUM_OF_MASKS - Number of masks per key extraction + */ +#define DPKG_NUM_OF_MASKS 4 + +/** + * DPKG_MAX_NUM_OF_EXTRACTS - Number of extractions per key profile + */ +#define DPKG_MAX_NUM_OF_EXTRACTS 10 + +/** + * enum dpkg_extract_from_hdr_type - Selecting extraction by header types + * @DPKG_FROM_HDR: Extract selected bytes from header, by offset + * @DPKG_FROM_FIELD: Extract selected bytes from header, by offset from field + * @DPKG_FULL_FIELD: Extract a full field + */ +enum dpkg_extract_from_hdr_type { + DPKG_FROM_HDR = 0, + DPKG_FROM_FIELD = 1, + DPKG_FULL_FIELD = 2 +}; + +/** + * enum dpkg_extract_type - Enumeration for selecting extraction type + * @DPKG_EXTRACT_FROM_HDR: Extract from the header + * @DPKG_EXTRACT_FROM_DATA: Extract from data not in specific header + * @DPKG_EXTRACT_FROM_PARSE: Extract from parser-result; + * e.g. can be used to extract header existence; + * please refer to 'Parse Result definition' section in the parser BG + */ +enum dpkg_extract_type { + DPKG_EXTRACT_FROM_HDR = 0, + DPKG_EXTRACT_FROM_DATA = 1, + DPKG_EXTRACT_FROM_PARSE = 3 +}; + +/** + * struct dpkg_mask - A structure for defining a single extraction mask + * @mask: Byte mask for the extracted content + * @offset: Offset within the extracted content + */ +struct dpkg_mask { + uint8_t mask; + uint8_t offset; +}; + +/* Protocol fields */ + +/* Ethernet fields */ +#define NH_FLD_ETH_DA BIT(0) +#define NH_FLD_ETH_SA BIT(1) +#define NH_FLD_ETH_LENGTH BIT(2) +#define NH_FLD_ETH_TYPE BIT(3) +#define NH_FLD_ETH_FINAL_CKSUM BIT(4) +#define NH_FLD_ETH_PADDING BIT(5) +#define NH_FLD_ETH_ALL_FIELDS (BIT(6) - 1) + +/* VLAN fields */ +#define NH_FLD_VLAN_VPRI BIT(0) +#define NH_FLD_VLAN_CFI BIT(1) +#define NH_FLD_VLAN_VID BIT(2) +#define NH_FLD_VLAN_LENGTH BIT(3) +#define NH_FLD_VLAN_TYPE BIT(4) +#define NH_FLD_VLAN_ALL_FIELDS (BIT(5) - 1) + +#define NH_FLD_VLAN_TCI (NH_FLD_VLAN_VPRI | \ + NH_FLD_VLAN_CFI | \ + NH_FLD_VLAN_VID) + +/* IP (generic) fields */ +#define NH_FLD_IP_VER BIT(0) +#define NH_FLD_IP_DSCP BIT(2) +#define NH_FLD_IP_ECN BIT(3) +#define NH_FLD_IP_PROTO BIT(4) +#define NH_FLD_IP_SRC BIT(5) +#define NH_FLD_IP_DST BIT(6) +#define NH_FLD_IP_TOS_TC BIT(7) +#define NH_FLD_IP_ID BIT(8) +#define NH_FLD_IP_ALL_FIELDS (BIT(9) - 1) + +/* IPV4 fields */ +#define NH_FLD_IPV4_VER BIT(0) +#define NH_FLD_IPV4_HDR_LEN BIT(1) +#define NH_FLD_IPV4_TOS BIT(2) +#define NH_FLD_IPV4_TOTAL_LEN BIT(3) +#define NH_FLD_IPV4_ID BIT(4) +#define NH_FLD_IPV4_FLAG_D BIT(5) +#define NH_FLD_IPV4_FLAG_M BIT(6) +#define NH_FLD_IPV4_OFFSET BIT(7) +#define NH_FLD_IPV4_TTL BIT(8) +#define NH_FLD_IPV4_PROTO BIT(9) +#define NH_FLD_IPV4_CKSUM BIT(10) +#define NH_FLD_IPV4_SRC_IP BIT(11) +#define NH_FLD_IPV4_DST_IP BIT(12) +#define NH_FLD_IPV4_OPTS BIT(13) +#define NH_FLD_IPV4_OPTS_COUNT BIT(14) +#define NH_FLD_IPV4_ALL_FIELDS (BIT(15) - 1) + +/* IPV6 fields */ +#define NH_FLD_IPV6_VER BIT(0) +#define NH_FLD_IPV6_TC BIT(1) +#define NH_FLD_IPV6_SRC_IP BIT(2) +#define NH_FLD_IPV6_DST_IP BIT(3) +#define NH_FLD_IPV6_NEXT_HDR BIT(4) +#define NH_FLD_IPV6_FL BIT(5) +#define NH_FLD_IPV6_HOP_LIMIT BIT(6) +#define NH_FLD_IPV6_ID BIT(7) +#define NH_FLD_IPV6_ALL_FIELDS (BIT(8) - 1) + +/* ICMP fields */ +#define NH_FLD_ICMP_TYPE BIT(0) +#define NH_FLD_ICMP_CODE BIT(1) +#define NH_FLD_ICMP_CKSUM BIT(2) +#define NH_FLD_ICMP_ID BIT(3) +#define NH_FLD_ICMP_SQ_NUM BIT(4) +#define NH_FLD_ICMP_ALL_FIELDS (BIT(5) - 1) + +/* IGMP fields */ +#define NH_FLD_IGMP_VERSION BIT(0) +#define NH_FLD_IGMP_TYPE BIT(1) +#define NH_FLD_IGMP_CKSUM BIT(2) +#define NH_FLD_IGMP_DATA BIT(3) +#define NH_FLD_IGMP_ALL_FIELDS (BIT(4) - 1) + +/* TCP fields */ +#define NH_FLD_TCP_PORT_SRC BIT(0) +#define NH_FLD_TCP_PORT_DST BIT(1) +#define NH_FLD_TCP_SEQ BIT(2) +#define NH_FLD_TCP_ACK BIT(3) +#define NH_FLD_TCP_OFFSET BIT(4) +#define NH_FLD_TCP_FLAGS BIT(5) +#define NH_FLD_TCP_WINDOW BIT(6) +#define NH_FLD_TCP_CKSUM BIT(7) +#define NH_FLD_TCP_URGPTR BIT(8) +#define NH_FLD_TCP_OPTS BIT(9) +#define NH_FLD_TCP_OPTS_COUNT BIT(10) +#define NH_FLD_TCP_ALL_FIELDS (BIT(11) - 1) + +/* UDP fields */ +#define NH_FLD_UDP_PORT_SRC BIT(0) +#define NH_FLD_UDP_PORT_DST BIT(1) +#define NH_FLD_UDP_LEN BIT(2) +#define NH_FLD_UDP_CKSUM BIT(3) +#define NH_FLD_UDP_ALL_FIELDS (BIT(4) - 1) + +/* UDP-lite fields */ +#define NH_FLD_UDP_LITE_PORT_SRC BIT(0) +#define NH_FLD_UDP_LITE_PORT_DST BIT(1) +#define NH_FLD_UDP_LITE_ALL_FIELDS (BIT(2) - 1) + +/* UDP-encap-ESP fields */ +#define NH_FLD_UDP_ENC_ESP_PORT_SRC BIT(0) +#define NH_FLD_UDP_ENC_ESP_PORT_DST BIT(1) +#define NH_FLD_UDP_ENC_ESP_LEN BIT(2) +#define NH_FLD_UDP_ENC_ESP_CKSUM BIT(3) +#define NH_FLD_UDP_ENC_ESP_SPI BIT(4) +#define NH_FLD_UDP_ENC_ESP_SEQUENCE_NUM BIT(5) +#define NH_FLD_UDP_ENC_ESP_ALL_FIELDS (BIT(6) - 1) + +/* SCTP fields */ +#define NH_FLD_SCTP_PORT_SRC BIT(0) +#define NH_FLD_SCTP_PORT_DST BIT(1) +#define NH_FLD_SCTP_VER_TAG BIT(2) +#define NH_FLD_SCTP_CKSUM BIT(3) +#define NH_FLD_SCTP_ALL_FIELDS (BIT(4) - 1) + +/* DCCP fields */ +#define NH_FLD_DCCP_PORT_SRC BIT(0) +#define NH_FLD_DCCP_PORT_DST BIT(1) +#define NH_FLD_DCCP_ALL_FIELDS (BIT(2) - 1) + +/* IPHC fields */ +#define NH_FLD_IPHC_CID BIT(0) +#define NH_FLD_IPHC_CID_TYPE BIT(1) +#define NH_FLD_IPHC_HCINDEX BIT(2) +#define NH_FLD_IPHC_GEN BIT(3) +#define NH_FLD_IPHC_D_BIT BIT(4) +#define NH_FLD_IPHC_ALL_FIELDS (BIT(5) - 1) + +/* SCTP fields */ +#define NH_FLD_SCTP_CHUNK_DATA_TYPE BIT(0) +#define NH_FLD_SCTP_CHUNK_DATA_FLAGS BIT(1) +#define NH_FLD_SCTP_CHUNK_DATA_LENGTH BIT(2) +#define NH_FLD_SCTP_CHUNK_DATA_TSN BIT(3) +#define NH_FLD_SCTP_CHUNK_DATA_STREAM_ID BIT(4) +#define NH_FLD_SCTP_CHUNK_DATA_STREAM_SQN BIT(5) +#define NH_FLD_SCTP_CHUNK_DATA_PAYLOAD_PID BIT(6) +#define NH_FLD_SCTP_CHUNK_DATA_UNORDERED BIT(7) +#define NH_FLD_SCTP_CHUNK_DATA_BEGGINING BIT(8) +#define NH_FLD_SCTP_CHUNK_DATA_END BIT(9) +#define NH_FLD_SCTP_CHUNK_DATA_ALL_FIELDS (BIT(10) - 1) + +/* L2TPV2 fields */ +#define NH_FLD_L2TPV2_TYPE_BIT BIT(0) +#define NH_FLD_L2TPV2_LENGTH_BIT BIT(1) +#define NH_FLD_L2TPV2_SEQUENCE_BIT BIT(2) +#define NH_FLD_L2TPV2_OFFSET_BIT BIT(3) +#define NH_FLD_L2TPV2_PRIORITY_BIT BIT(4) +#define NH_FLD_L2TPV2_VERSION BIT(5) +#define NH_FLD_L2TPV2_LEN BIT(6) +#define NH_FLD_L2TPV2_TUNNEL_ID BIT(7) +#define NH_FLD_L2TPV2_SESSION_ID BIT(8) +#define NH_FLD_L2TPV2_NS BIT(9) +#define NH_FLD_L2TPV2_NR BIT(10) +#define NH_FLD_L2TPV2_OFFSET_SIZE BIT(11) +#define NH_FLD_L2TPV2_FIRST_BYTE BIT(12) +#define NH_FLD_L2TPV2_ALL_FIELDS (BIT(13) - 1) + +/* L2TPV3 fields */ +#define NH_FLD_L2TPV3_CTRL_TYPE_BIT BIT(0) +#define NH_FLD_L2TPV3_CTRL_LENGTH_BIT BIT(1) +#define NH_FLD_L2TPV3_CTRL_SEQUENCE_BIT BIT(2) +#define NH_FLD_L2TPV3_CTRL_VERSION BIT(3) +#define NH_FLD_L2TPV3_CTRL_LENGTH BIT(4) +#define NH_FLD_L2TPV3_CTRL_CONTROL BIT(5) +#define NH_FLD_L2TPV3_CTRL_SENT BIT(6) +#define NH_FLD_L2TPV3_CTRL_RECV BIT(7) +#define NH_FLD_L2TPV3_CTRL_FIRST_BYTE BIT(8) +#define NH_FLD_L2TPV3_CTRL_ALL_FIELDS (BIT(9) - 1) + +#define NH_FLD_L2TPV3_SESS_TYPE_BIT BIT(0) +#define NH_FLD_L2TPV3_SESS_VERSION BIT(1) +#define NH_FLD_L2TPV3_SESS_ID BIT(2) +#define NH_FLD_L2TPV3_SESS_COOKIE BIT(3) +#define NH_FLD_L2TPV3_SESS_ALL_FIELDS (BIT(4) - 1) + +/* PPP fields */ +#define NH_FLD_PPP_PID BIT(0) +#define NH_FLD_PPP_COMPRESSED BIT(1) +#define NH_FLD_PPP_ALL_FIELDS (BIT(2) - 1) + +/* PPPoE fields */ +#define NH_FLD_PPPOE_VER BIT(0) +#define NH_FLD_PPPOE_TYPE BIT(1) +#define NH_FLD_PPPOE_CODE BIT(2) +#define NH_FLD_PPPOE_SID BIT(3) +#define NH_FLD_PPPOE_LEN BIT(4) +#define NH_FLD_PPPOE_SESSION BIT(5) +#define NH_FLD_PPPOE_PID BIT(6) +#define NH_FLD_PPPOE_ALL_FIELDS (BIT(7) - 1) + +/* PPP-Mux fields */ +#define NH_FLD_PPPMUX_PID BIT(0) +#define NH_FLD_PPPMUX_CKSUM BIT(1) +#define NH_FLD_PPPMUX_COMPRESSED BIT(2) +#define NH_FLD_PPPMUX_ALL_FIELDS (BIT(3) - 1) + +/* PPP-Mux sub-frame fields */ +#define NH_FLD_PPPMUX_SUBFRM_PFF BIT(0) +#define NH_FLD_PPPMUX_SUBFRM_LXT BIT(1) +#define NH_FLD_PPPMUX_SUBFRM_LEN BIT(2) +#define NH_FLD_PPPMUX_SUBFRM_PID BIT(3) +#define NH_FLD_PPPMUX_SUBFRM_USE_PID BIT(4) +#define NH_FLD_PPPMUX_SUBFRM_ALL_FIELDS (BIT(5) - 1) + +/* LLC fields */ +#define NH_FLD_LLC_DSAP BIT(0) +#define NH_FLD_LLC_SSAP BIT(1) +#define NH_FLD_LLC_CTRL BIT(2) +#define NH_FLD_LLC_ALL_FIELDS (BIT(3) - 1) + +/* NLPID fields */ +#define NH_FLD_NLPID_NLPID BIT(0) +#define NH_FLD_NLPID_ALL_FIELDS (BIT(1) - 1) + +/* SNAP fields */ +#define NH_FLD_SNAP_OUI BIT(0) +#define NH_FLD_SNAP_PID BIT(1) +#define NH_FLD_SNAP_ALL_FIELDS (BIT(2) - 1) + +/* LLC SNAP fields */ +#define NH_FLD_LLC_SNAP_TYPE BIT(0) +#define NH_FLD_LLC_SNAP_ALL_FIELDS (BIT(1) - 1) + +/* ARP fields */ +#define NH_FLD_ARP_HTYPE BIT(0) +#define NH_FLD_ARP_PTYPE BIT(1) +#define NH_FLD_ARP_HLEN BIT(2) +#define NH_FLD_ARP_PLEN BIT(3) +#define NH_FLD_ARP_OPER BIT(4) +#define NH_FLD_ARP_SHA BIT(5) +#define NH_FLD_ARP_SPA BIT(6) +#define NH_FLD_ARP_THA BIT(7) +#define NH_FLD_ARP_TPA BIT(8) +#define NH_FLD_ARP_ALL_FIELDS (BIT(9) - 1) + +/* RFC2684 fields */ +#define NH_FLD_RFC2684_LLC BIT(0) +#define NH_FLD_RFC2684_NLPID BIT(1) +#define NH_FLD_RFC2684_OUI BIT(2) +#define NH_FLD_RFC2684_PID BIT(3) +#define NH_FLD_RFC2684_VPN_OUI BIT(4) +#define NH_FLD_RFC2684_VPN_IDX BIT(5) +#define NH_FLD_RFC2684_ALL_FIELDS (BIT(6) - 1) + +/* User defined fields */ +#define NH_FLD_USER_DEFINED_SRCPORT BIT(0) +#define NH_FLD_USER_DEFINED_PCDID BIT(1) +#define NH_FLD_USER_DEFINED_ALL_FIELDS (BIT(2) - 1) + +/* Payload fields */ +#define NH_FLD_PAYLOAD_BUFFER BIT(0) +#define NH_FLD_PAYLOAD_SIZE BIT(1) +#define NH_FLD_MAX_FRM_SIZE BIT(2) +#define NH_FLD_MIN_FRM_SIZE BIT(3) +#define NH_FLD_PAYLOAD_TYPE BIT(4) +#define NH_FLD_FRAME_SIZE BIT(5) +#define NH_FLD_PAYLOAD_ALL_FIELDS (BIT(6) - 1) + +/* GRE fields */ +#define NH_FLD_GRE_TYPE BIT(0) +#define NH_FLD_GRE_ALL_FIELDS (BIT(1) - 1) + +/* MINENCAP fields */ +#define NH_FLD_MINENCAP_SRC_IP BIT(0) +#define NH_FLD_MINENCAP_DST_IP BIT(1) +#define NH_FLD_MINENCAP_TYPE BIT(2) +#define NH_FLD_MINENCAP_ALL_FIELDS (BIT(3) - 1) + +/* IPSEC AH fields */ +#define NH_FLD_IPSEC_AH_SPI BIT(0) +#define NH_FLD_IPSEC_AH_NH BIT(1) +#define NH_FLD_IPSEC_AH_ALL_FIELDS (BIT(2) - 1) + +/* IPSEC ESP fields */ +#define NH_FLD_IPSEC_ESP_SPI BIT(0) +#define NH_FLD_IPSEC_ESP_SEQUENCE_NUM BIT(1) +#define NH_FLD_IPSEC_ESP_ALL_FIELDS (BIT(2) - 1) + +/* MPLS fields */ +#define NH_FLD_MPLS_LABEL_STACK BIT(0) +#define NH_FLD_MPLS_LABEL_STACK_ALL_FIELDS (BIT(1) - 1) + +/* MACSEC fields */ +#define NH_FLD_MACSEC_SECTAG BIT(0) +#define NH_FLD_MACSEC_ALL_FIELDS (BIT(1) - 1) + +/* GTP fields */ +#define NH_FLD_GTP_TEID BIT(0) + +/* Supported protocols */ +enum net_prot { + NET_PROT_NONE = 0, + NET_PROT_PAYLOAD, + NET_PROT_ETH, + NET_PROT_VLAN, + NET_PROT_IPV4, + NET_PROT_IPV6, + NET_PROT_IP, + NET_PROT_TCP, + NET_PROT_UDP, + NET_PROT_UDP_LITE, + NET_PROT_IPHC, + NET_PROT_SCTP, + NET_PROT_SCTP_CHUNK_DATA, + NET_PROT_PPPOE, + NET_PROT_PPP, + NET_PROT_PPPMUX, + NET_PROT_PPPMUX_SUBFRM, + NET_PROT_L2TPV2, + NET_PROT_L2TPV3_CTRL, + NET_PROT_L2TPV3_SESS, + NET_PROT_LLC, + NET_PROT_LLC_SNAP, + NET_PROT_NLPID, + NET_PROT_SNAP, + NET_PROT_MPLS, + NET_PROT_IPSEC_AH, + NET_PROT_IPSEC_ESP, + NET_PROT_UDP_ENC_ESP, /* RFC 3948 */ + NET_PROT_MACSEC, + NET_PROT_GRE, + NET_PROT_MINENCAP, + NET_PROT_DCCP, + NET_PROT_ICMP, + NET_PROT_IGMP, + NET_PROT_ARP, + NET_PROT_CAPWAP_DATA, + NET_PROT_CAPWAP_CTRL, + NET_PROT_RFC2684, + NET_PROT_ICMPV6, + NET_PROT_FCOE, + NET_PROT_FIP, + NET_PROT_ISCSI, + NET_PROT_GTP, + NET_PROT_USER_DEFINED_L2, + NET_PROT_USER_DEFINED_L3, + NET_PROT_USER_DEFINED_L4, + NET_PROT_USER_DEFINED_L5, + NET_PROT_USER_DEFINED_SHIM1, + NET_PROT_USER_DEFINED_SHIM2, + + NET_PROT_DUMMY_LAST +}; + +/** + * struct dpkg_extract - A structure for defining a single extraction + * @type: Determines how the union below is interpreted: + * DPKG_EXTRACT_FROM_HDR: selects 'from_hdr'; + * DPKG_EXTRACT_FROM_DATA: selects 'from_data'; + * DPKG_EXTRACT_FROM_PARSE: selects 'from_parse' + * @extract: Selects extraction method + * @extract.from_hdr: Used when 'type = DPKG_EXTRACT_FROM_HDR' + * @extract.from_data: Used when 'type = DPKG_EXTRACT_FROM_DATA' + * @extract.from_parse: Used when 'type = DPKG_EXTRACT_FROM_PARSE' + * @extract.from_hdr.prot: Any of the supported headers + * @extract.from_hdr.type: Defines the type of header extraction: + * DPKG_FROM_HDR: use size & offset below; + * DPKG_FROM_FIELD: use field, size and offset below; + * DPKG_FULL_FIELD: use field below + * @extract.from_hdr.field: One of the supported fields (NH_FLD_) + * @extract.from_hdr.size: Size in bytes + * @extract.from_hdr.offset: Byte offset + * @extract.from_hdr.hdr_index: Clear for cases not listed below; + * Used for protocols that may have more than a single + * header, 0 indicates an outer header; + * Supported protocols (possible values): + * NET_PROT_VLAN (0, HDR_INDEX_LAST); + * NET_PROT_MPLS (0, 1, HDR_INDEX_LAST); + * NET_PROT_IP(0, HDR_INDEX_LAST); + * NET_PROT_IPv4(0, HDR_INDEX_LAST); + * NET_PROT_IPv6(0, HDR_INDEX_LAST); + * @extract.from_data.size: Size in bytes + * @extract.from_data.offset: Byte offset + * @extract.from_parse.size: Size in bytes + * @extract.from_parse.offset: Byte offset + * @num_of_byte_masks: Defines the number of valid entries in the array below; + * This is also the number of bytes to be used as masks + * @masks: Masks parameters + */ +struct dpkg_extract { + enum dpkg_extract_type type; + union { + struct { + enum net_prot prot; + enum dpkg_extract_from_hdr_type type; + uint32_t field; + uint8_t size; + uint8_t offset; + uint8_t hdr_index; + } from_hdr; + struct { + uint8_t size; + uint8_t offset; + } from_data; + struct { + uint8_t size; + uint8_t offset; + } from_parse; + } extract; + + uint8_t num_of_byte_masks; + struct dpkg_mask masks[DPKG_NUM_OF_MASKS]; +}; + +/** + * struct dpkg_profile_cfg - A structure for defining a full Key Generation + * profile (rule) + * @num_extracts: Defines the number of valid entries in the array below + * @extracts: Array of required extractions + */ +struct dpkg_profile_cfg { + uint8_t num_extracts; + struct dpkg_extract extracts[DPKG_MAX_NUM_OF_EXTRACTS]; +}; + +#endif /* _DPAA2_NI_DPKG_H */ diff --git a/sys/dev/dpaa2/dpaa2_rc.c b/sys/dev/dpaa2/dpaa2_rc.c new file mode 100644 index 000000000000..f5d7bae92e04 --- /dev/null +++ b/sys/dev/dpaa2/dpaa2_rc.c @@ -0,0 +1,3585 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright © 2021-2022 Dmitry Salychev + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +/* + * The DPAA2 Resource Container (DPRC) bus driver. + * + * DPRC holds all the resources and object information that a software context + * (kernel, virtual machine, etc.) can access or use. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "pcib_if.h" +#include "pci_if.h" + +#include "dpaa2_mcp.h" +#include "dpaa2_mc.h" +#include "dpaa2_ni.h" +#include "dpaa2_mc_if.h" +#include "dpaa2_cmd_if.h" + +/* Timeouts to wait for a command response from MC. */ +#define CMD_SPIN_TIMEOUT 100u /* us */ +#define CMD_SPIN_ATTEMPTS 2000u /* max. 200 ms */ + +#define TYPE_LEN_MAX 16u +#define LABEL_LEN_MAX 16u + +MALLOC_DEFINE(M_DPAA2_RC, "dpaa2_rc", "DPAA2 Resource Container"); + +/* Discover and add devices to the resource container. */ +static int dpaa2_rc_discover(struct dpaa2_rc_softc *); +static int dpaa2_rc_add_child(struct dpaa2_rc_softc *, struct dpaa2_cmd *, + struct dpaa2_obj *); +static int dpaa2_rc_add_managed_child(struct dpaa2_rc_softc *, + struct dpaa2_cmd *, struct dpaa2_obj *); + +/* Helper routines. */ +static int dpaa2_rc_enable_irq(struct dpaa2_mcp *, struct dpaa2_cmd *, uint8_t, + bool, uint16_t); +static int dpaa2_rc_configure_irq(device_t, device_t, int, uint64_t, uint32_t); +static int dpaa2_rc_add_res(device_t, device_t, enum dpaa2_dev_type, int *, int); +static int dpaa2_rc_print_type(struct resource_list *, enum dpaa2_dev_type); +static struct dpaa2_mcp *dpaa2_rc_select_portal(device_t, device_t); + +/* Routines to send commands to MC. */ +static int dpaa2_rc_exec_cmd(struct dpaa2_mcp *, struct dpaa2_cmd *, uint16_t); +static int dpaa2_rc_send_cmd(struct dpaa2_mcp *, struct dpaa2_cmd *); +static int dpaa2_rc_wait_for_cmd(struct dpaa2_mcp *, struct dpaa2_cmd *); +static int dpaa2_rc_reset_cmd_params(struct dpaa2_cmd *); + +static int +dpaa2_rc_probe(device_t dev) +{ + /* DPRC device will be added by the parent DPRC or MC bus itself. */ + device_set_desc(dev, "DPAA2 Resource Container"); + return (BUS_PROBE_DEFAULT); +} + +static int +dpaa2_rc_detach(device_t dev) +{ + struct dpaa2_devinfo *dinfo; + int error; + + error = bus_generic_detach(dev); + if (error) + return (error); + + dinfo = device_get_ivars(dev); + + if (dinfo->portal) + dpaa2_mcp_free_portal(dinfo->portal); + if (dinfo) + free(dinfo, M_DPAA2_RC); + + return (device_delete_children(dev)); +} + +static int +dpaa2_rc_attach(device_t dev) +{ + device_t pdev; + struct dpaa2_mc_softc *mcsc; + struct dpaa2_rc_softc *sc; + struct dpaa2_devinfo *dinfo = NULL; + int error; + + sc = device_get_softc(dev); + sc->dev = dev; + sc->unit = device_get_unit(dev); + + if (sc->unit == 0) { + /* Root DPRC should be attached directly to the MC bus. */ + pdev = device_get_parent(dev); + mcsc = device_get_softc(pdev); + + KASSERT(strcmp(device_get_name(pdev), "dpaa2_mc") == 0, + ("root DPRC should be attached to the MC bus")); + + /* + * Allocate devinfo to let the parent MC bus access ICID of the + * DPRC object. + */ + dinfo = malloc(sizeof(struct dpaa2_devinfo), M_DPAA2_RC, + M_WAITOK | M_ZERO); + if (!dinfo) { + device_printf(dev, "%s: failed to allocate " + "dpaa2_devinfo\n", __func__); + dpaa2_rc_detach(dev); + return (ENXIO); + } + device_set_ivars(dev, dinfo); + + dinfo->pdev = pdev; + dinfo->dev = dev; + dinfo->dtype = DPAA2_DEV_RC; + dinfo->portal = NULL; + + /* Prepare helper portal object to send commands to MC. */ + error = dpaa2_mcp_init_portal(&dinfo->portal, mcsc->res[0], + &mcsc->map[0], DPAA2_PORTAL_DEF); + if (error) { + device_printf(dev, "%s: failed to initialize dpaa2_mcp: " + "error=%d\n", __func__, error); + dpaa2_rc_detach(dev); + return (ENXIO); + } + } else { + /* TODO: Child DPRCs aren't supported yet. */ + return (ENXIO); + } + + /* Create DPAA2 devices for objects in this container. */ + error = dpaa2_rc_discover(sc); + if (error) { + device_printf(dev, "%s: failed to discover objects in " + "container: error=%d\n", __func__, error); + dpaa2_rc_detach(dev); + return (error); + } + + return (0); +} + +/* + * Bus interface. + */ + +static struct resource_list * +dpaa2_rc_get_resource_list(device_t rcdev, device_t child) +{ + struct dpaa2_devinfo *dinfo = device_get_ivars(child); + + return (&dinfo->resources); +} + +static void +dpaa2_rc_delete_resource(device_t rcdev, device_t child, int type, int rid) +{ + struct resource_list *rl; + struct resource_list_entry *rle; + struct dpaa2_devinfo *dinfo; + + if (device_get_parent(child) != rcdev) + return; + + dinfo = device_get_ivars(child); + rl = &dinfo->resources; + rle = resource_list_find(rl, type, rid); + if (rle == NULL) + return; + + if (rle->res) { + if (rman_get_flags(rle->res) & RF_ACTIVE || + resource_list_busy(rl, type, rid)) { + device_printf(rcdev, "%s: resource still owned by " + "child: type=%d, rid=%d, start=%jx\n", __func__, + type, rid, rman_get_start(rle->res)); + return; + } + resource_list_unreserve(rl, rcdev, child, type, rid); + } + resource_list_delete(rl, type, rid); +} + +static struct resource * +dpaa2_rc_alloc_multi_resource(device_t rcdev, device_t child, int type, int *rid, + rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) +{ + struct resource_list *rl; + struct dpaa2_devinfo *dinfo; + + dinfo = device_get_ivars(child); + rl = &dinfo->resources; + + /* + * By default, software portal interrupts are message-based, that is, + * they are issued from QMan using a 4 byte write. + * + * TODO: However this default behavior can be changed by programming one + * or more software portals to issue their interrupts via a + * dedicated software portal interrupt wire. + * See registers SWP_INTW0_CFG to SWP_INTW3_CFG for details. + */ + if (type == SYS_RES_IRQ && *rid == 0) + return (NULL); + + return (resource_list_alloc(rl, rcdev, child, type, rid, + start, end, count, flags)); +} + +static struct resource * +dpaa2_rc_alloc_resource(device_t rcdev, device_t child, int type, int *rid, + rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) +{ + if (device_get_parent(child) != rcdev) + return (BUS_ALLOC_RESOURCE(device_get_parent(rcdev), child, + type, rid, start, end, count, flags)); + + return (dpaa2_rc_alloc_multi_resource(rcdev, child, type, rid, start, + end, count, flags)); +} + +static int +dpaa2_rc_release_resource(device_t rcdev, device_t child, int type, int rid, + struct resource *r) +{ + struct resource_list *rl; + struct dpaa2_devinfo *dinfo; + + if (device_get_parent(child) != rcdev) + return (BUS_RELEASE_RESOURCE(device_get_parent(rcdev), child, + type, rid, r)); + + dinfo = device_get_ivars(child); + rl = &dinfo->resources; + return (resource_list_release(rl, rcdev, child, type, rid, r)); +} + +static void +dpaa2_rc_child_deleted(device_t rcdev, device_t child) +{ + struct dpaa2_devinfo *dinfo; + struct resource_list *rl; + struct resource_list_entry *rle; + + dinfo = device_get_ivars(child); + rl = &dinfo->resources; + + /* Free all allocated resources */ + STAILQ_FOREACH(rle, rl, link) { + if (rle->res) { + if (rman_get_flags(rle->res) & RF_ACTIVE || + resource_list_busy(rl, rle->type, rle->rid)) { + device_printf(child, "%s: resource still owned: " + "type=%d, rid=%d, addr=%lx\n", __func__, + rle->type, rle->rid, + rman_get_start(rle->res)); + bus_release_resource(child, rle->type, rle->rid, + rle->res); + } + resource_list_unreserve(rl, rcdev, child, rle->type, + rle->rid); + } + } + resource_list_free(rl); + + if (dinfo) + free(dinfo, M_DPAA2_RC); +} + +static void +dpaa2_rc_child_detached(device_t rcdev, device_t child) +{ + struct dpaa2_devinfo *dinfo; + struct resource_list *rl; + + dinfo = device_get_ivars(child); + rl = &dinfo->resources; + + if (resource_list_release_active(rl, rcdev, child, SYS_RES_IRQ) != 0) + device_printf(child, "%s: leaked IRQ resources!\n", __func__); + if (dinfo->msi.msi_alloc != 0) { + device_printf(child, "%s: leaked %d MSI vectors!\n", __func__, + dinfo->msi.msi_alloc); + PCI_RELEASE_MSI(rcdev, child); + } + if (resource_list_release_active(rl, rcdev, child, SYS_RES_MEMORY) != 0) + device_printf(child, "%s: leaked memory resources!\n", __func__); +} + +static int +dpaa2_rc_setup_intr(device_t rcdev, device_t child, struct resource *irq, + int flags, driver_filter_t *filter, driver_intr_t *intr, void *arg, + void **cookiep) +{ + struct dpaa2_devinfo *dinfo; + uint64_t addr; + uint32_t data; + void *cookie; + int error, rid; + + error = bus_generic_setup_intr(rcdev, child, irq, flags, filter, intr, + arg, &cookie); + if (error) { + device_printf(rcdev, "%s: bus_generic_setup_intr() failed: " + "error=%d\n", __func__, error); + return (error); + } + + /* If this is not a direct child, just bail out. */ + if (device_get_parent(child) != rcdev) { + *cookiep = cookie; + return (0); + } + + rid = rman_get_rid(irq); + if (rid == 0) { + if (bootverbose) + device_printf(rcdev, "%s: cannot setup interrupt with " + "rid=0: INTx are not supported by DPAA2 objects " + "yet\n", __func__); + return (EINVAL); + } else { + dinfo = device_get_ivars(child); + KASSERT(dinfo->msi.msi_alloc > 0, + ("No MSI interrupts allocated")); + + /* + * Ask our parent to map the MSI and give us the address and + * data register values. If we fail for some reason, teardown + * the interrupt handler. + */ + error = PCIB_MAP_MSI(device_get_parent(rcdev), child, + rman_get_start(irq), &addr, &data); + if (error) { + device_printf(rcdev, "%s: PCIB_MAP_MSI failed: " + "error=%d\n", __func__, error); + (void)bus_generic_teardown_intr(rcdev, child, irq, + cookie); + return (error); + } + + /* Configure MSI for this DPAA2 object. */ + error = dpaa2_rc_configure_irq(rcdev, child, rid, addr, data); + if (error) { + device_printf(rcdev, "%s: failed to configure IRQ for " + "DPAA2 object: rid=%d, type=%s, unit=%d\n", __func__, + rid, dpaa2_ttos(dinfo->dtype), + device_get_unit(child)); + return (error); + } + dinfo->msi.msi_handlers++; + } + *cookiep = cookie; + return (0); +} + +static int +dpaa2_rc_teardown_intr(device_t rcdev, device_t child, struct resource *irq, + void *cookie) +{ + struct resource_list_entry *rle; + struct dpaa2_devinfo *dinfo; + int error, rid; + + if (irq == NULL || !(rman_get_flags(irq) & RF_ACTIVE)) + return (EINVAL); + + /* If this isn't a direct child, just bail out */ + if (device_get_parent(child) != rcdev) + return(bus_generic_teardown_intr(rcdev, child, irq, cookie)); + + rid = rman_get_rid(irq); + if (rid == 0) { + if (bootverbose) + device_printf(rcdev, "%s: cannot teardown interrupt " + "with rid=0: INTx are not supported by DPAA2 " + "objects yet\n", __func__); + return (EINVAL); + } else { + dinfo = device_get_ivars(child); + rle = resource_list_find(&dinfo->resources, SYS_RES_IRQ, rid); + if (rle->res != irq) + return (EINVAL); + dinfo->msi.msi_handlers--; + } + + error = bus_generic_teardown_intr(rcdev, child, irq, cookie); + if (rid > 0) + KASSERT(error == 0, + ("%s: generic teardown failed for MSI", __func__)); + return (error); +} + +static int +dpaa2_rc_print_child(device_t rcdev, device_t child) +{ + struct dpaa2_devinfo *dinfo = device_get_ivars(child); + struct resource_list *rl = &dinfo->resources; + int retval = 0; + + retval += bus_print_child_header(rcdev, child); + + retval += resource_list_print_type(rl, "port", SYS_RES_IOPORT, "%#jx"); + retval += resource_list_print_type(rl, "iomem", SYS_RES_MEMORY, "%#jx"); + retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%jd"); + + /* Print DPAA2-specific resources. */ + retval += dpaa2_rc_print_type(rl, DPAA2_DEV_IO); + retval += dpaa2_rc_print_type(rl, DPAA2_DEV_BP); + retval += dpaa2_rc_print_type(rl, DPAA2_DEV_CON); + retval += dpaa2_rc_print_type(rl, DPAA2_DEV_MCP); + + retval += printf(" at %s (id=%u)", dpaa2_ttos(dinfo->dtype), dinfo->id); + + retval += bus_print_child_domain(rcdev, child); + retval += bus_print_child_footer(rcdev, child); + + return (retval); +} + +/* + * Pseudo-PCI interface. + */ + +/* + * Attempt to allocate *count MSI messages. The actual number allocated is + * returned in *count. After this function returns, each message will be + * available to the driver as SYS_RES_IRQ resources starting at a rid 1. + * + * NOTE: Implementation is similar to sys/dev/pci/pci.c. + */ +static int +dpaa2_rc_alloc_msi(device_t rcdev, device_t child, int *count) +{ + struct dpaa2_devinfo *rcinfo = device_get_ivars(rcdev); + struct dpaa2_devinfo *dinfo = device_get_ivars(child); + int error, actual, i, run, irqs[32]; + + /* Don't let count == 0 get us into trouble. */ + if (*count == 0) + return (EINVAL); + + /* MSI should be allocated by the resource container. */ + if (rcinfo->dtype != DPAA2_DEV_RC) + return (ENODEV); + + /* Already have allocated messages? */ + if (dinfo->msi.msi_alloc != 0) + return (ENXIO); + + /* Don't ask for more than the device supports. */ + actual = min(*count, dinfo->msi.msi_msgnum); + + /* Don't ask for more than 32 messages. */ + actual = min(actual, 32); + + /* MSI requires power of 2 number of messages. */ + if (!powerof2(actual)) + return (EINVAL); + + for (;;) { + /* Try to allocate N messages. */ + error = PCIB_ALLOC_MSI(device_get_parent(rcdev), child, actual, + actual, irqs); + if (error == 0) + break; + if (actual == 1) + return (error); + + /* Try N / 2. */ + actual >>= 1; + } + + /* + * We now have N actual messages mapped onto SYS_RES_IRQ resources in + * the irqs[] array, so add new resources starting at rid 1. + */ + for (i = 0; i < actual; i++) + resource_list_add(&dinfo->resources, SYS_RES_IRQ, i + 1, + irqs[i], irqs[i], 1); + + if (bootverbose) { + if (actual == 1) { + device_printf(child, "using IRQ %d for MSI\n", irqs[0]); + } else { + /* + * Be fancy and try to print contiguous runs + * of IRQ values as ranges. 'run' is true if + * we are in a range. + */ + device_printf(child, "using IRQs %d", irqs[0]); + run = 0; + for (i = 1; i < actual; i++) { + /* Still in a run? */ + if (irqs[i] == irqs[i - 1] + 1) { + run = 1; + continue; + } + + /* Finish previous range. */ + if (run) { + printf("-%d", irqs[i - 1]); + run = 0; + } + + /* Start new range. */ + printf(",%d", irqs[i]); + } + + /* Unfinished range? */ + if (run) + printf("-%d", irqs[actual - 1]); + printf(" for MSI\n"); + } + } + + /* Update counts of alloc'd messages. */ + dinfo->msi.msi_alloc = actual; + dinfo->msi.msi_handlers = 0; + *count = actual; + return (0); +} + +/* + * Release the MSI messages associated with this DPAA2 device. + * + * NOTE: Implementation is similar to sys/dev/pci/pci.c. + */ +static int +dpaa2_rc_release_msi(device_t rcdev, device_t child) +{ + struct dpaa2_devinfo *rcinfo = device_get_ivars(rcdev); + struct dpaa2_devinfo *dinfo = device_get_ivars(child); + struct resource_list_entry *rle; + int i, irqs[32]; + + /* MSI should be released by the resource container. */ + if (rcinfo->dtype != DPAA2_DEV_RC) + return (ENODEV); + + /* Do we have any messages to release? */ + if (dinfo->msi.msi_alloc == 0) + return (ENODEV); + KASSERT(dinfo->msi.msi_alloc <= 32, + ("more than 32 alloc'd MSI messages")); + + /* Make sure none of the resources are allocated. */ + if (dinfo->msi.msi_handlers > 0) + return (EBUSY); + for (i = 0; i < dinfo->msi.msi_alloc; i++) { + rle = resource_list_find(&dinfo->resources, SYS_RES_IRQ, i + 1); + KASSERT(rle != NULL, ("missing MSI resource")); + if (rle->res != NULL) + return (EBUSY); + irqs[i] = rle->start; + } + + /* Release the messages. */ + PCIB_RELEASE_MSI(device_get_parent(rcdev), child, dinfo->msi.msi_alloc, + irqs); + for (i = 0; i < dinfo->msi.msi_alloc; i++) + resource_list_delete(&dinfo->resources, SYS_RES_IRQ, i + 1); + + /* Update alloc count. */ + dinfo->msi.msi_alloc = 0; + return (0); +} + +/** + * @brief Return the maximum number of the MSI supported by this DPAA2 device. + */ +static int +dpaa2_rc_msi_count(device_t rcdev, device_t child) +{ + struct dpaa2_devinfo *dinfo = device_get_ivars(child); + + return (dinfo->msi.msi_msgnum); +} + +static int +dpaa2_rc_get_id(device_t rcdev, device_t child, enum pci_id_type type, + uintptr_t *id) +{ + struct dpaa2_devinfo *rcinfo = device_get_ivars(rcdev); + + if (rcinfo->dtype != DPAA2_DEV_RC) + return (ENODEV); + + return (PCIB_GET_ID(device_get_parent(rcdev), child, type, id)); +} + +/* + * DPAA2 MC command interface. + */ + +static int +dpaa2_rc_mng_get_version(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint32_t *major, uint32_t *minor, uint32_t *rev) +{ + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + int error; + + if (portal == NULL || cmd == NULL || major == NULL || minor == NULL || + rev == NULL) + return (DPAA2_CMD_STAT_ERR); + + error = dpaa2_rc_exec_cmd(portal, cmd, CMDID_MNG_GET_VER); + if (!error) { + *major = cmd->params[0] >> 32; + *minor = cmd->params[1] & 0xFFFFFFFF; + *rev = cmd->params[0] & 0xFFFFFFFF; + } + + return (error); +} + +static int +dpaa2_rc_mng_get_soc_version(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint32_t *pvr, uint32_t *svr) +{ + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + int error; + + if (portal == NULL || cmd == NULL || pvr == NULL || svr == NULL) + return (DPAA2_CMD_STAT_ERR); + + error = dpaa2_rc_exec_cmd(portal, cmd, CMDID_MNG_GET_SOC_VER); + if (!error) { + *pvr = cmd->params[0] >> 32; + *svr = cmd->params[0] & 0xFFFFFFFF; + } + + return (error); +} + +static int +dpaa2_rc_mng_get_container_id(device_t dev, device_t child, + struct dpaa2_cmd *cmd, uint32_t *cont_id) +{ + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + int error; + + if (portal == NULL || cmd == NULL || cont_id == NULL) + return (DPAA2_CMD_STAT_ERR); + + error = dpaa2_rc_exec_cmd(portal, cmd, CMDID_MNG_GET_CONT_ID); + if (!error) + *cont_id = cmd->params[0] & 0xFFFFFFFF; + + return (error); +} + +static int +dpaa2_rc_open(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint32_t cont_id, uint16_t *token) +{ + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + struct dpaa2_cmd_header *hdr; + int error; + + if (portal == NULL || cmd == NULL || token == NULL) + return (DPAA2_CMD_STAT_ERR); + + cmd->params[0] = cont_id; + + error = dpaa2_rc_exec_cmd(portal, cmd, CMDID_RC_OPEN); + if (!error) { + hdr = (struct dpaa2_cmd_header *) &cmd->header; + *token = hdr->token; + } + + return (error); +} + +static int +dpaa2_rc_close(device_t dev, device_t child, struct dpaa2_cmd *cmd) +{ + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + + if (portal == NULL || cmd == NULL) + return (DPAA2_CMD_STAT_ERR); + + return (dpaa2_rc_exec_cmd(portal, cmd, CMDID_RC_CLOSE)); +} + +static int +dpaa2_rc_get_obj_count(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint32_t *obj_count) +{ + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + int error; + + if (portal == NULL || cmd == NULL || obj_count == NULL) + return (DPAA2_CMD_STAT_ERR); + + error = dpaa2_rc_exec_cmd(portal, cmd, CMDID_RC_GET_OBJ_COUNT); + if (!error) + *obj_count = (uint32_t)(cmd->params[0] >> 32); + + return (error); +} + +static int +dpaa2_rc_get_obj(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint32_t obj_idx, struct dpaa2_obj *obj) +{ + struct __packed dpaa2_obj_resp { + uint32_t _reserved1; + uint32_t id; + uint16_t vendor; + uint8_t irq_count; + uint8_t reg_count; + uint32_t state; + uint16_t ver_major; + uint16_t ver_minor; + uint16_t flags; + uint16_t _reserved2; + uint8_t type[16]; + uint8_t label[16]; + } *pobj; + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + int error; + + if (portal == NULL || cmd == NULL || obj == NULL) + return (DPAA2_CMD_STAT_ERR); + + cmd->params[0] = obj_idx; + + error = dpaa2_rc_exec_cmd(portal, cmd, CMDID_RC_GET_OBJ); + if (!error) { + pobj = (struct dpaa2_obj_resp *) &cmd->params[0]; + obj->id = pobj->id; + obj->vendor = pobj->vendor; + obj->irq_count = pobj->irq_count; + obj->reg_count = pobj->reg_count; + obj->state = pobj->state; + obj->ver_major = pobj->ver_major; + obj->ver_minor = pobj->ver_minor; + obj->flags = pobj->flags; + obj->type = dpaa2_stot((const char *) pobj->type); + memcpy(obj->label, pobj->label, sizeof(pobj->label)); + } + + /* Some DPAA2 objects might not be supported by the driver yet. */ + if (obj->type == DPAA2_DEV_NOTYPE) + error = DPAA2_CMD_STAT_UNKNOWN_OBJ; + + return (error); +} + +static int +dpaa2_rc_get_obj_descriptor(device_t dev, device_t child, + struct dpaa2_cmd *cmd, uint32_t obj_id, enum dpaa2_dev_type dtype, + struct dpaa2_obj *obj) +{ + struct __packed get_obj_desc_args { + uint32_t obj_id; + uint32_t _reserved1; + uint8_t type[16]; + } *args; + struct __packed dpaa2_obj_resp { + uint32_t _reserved1; + uint32_t id; + uint16_t vendor; + uint8_t irq_count; + uint8_t reg_count; + uint32_t state; + uint16_t ver_major; + uint16_t ver_minor; + uint16_t flags; + uint16_t _reserved2; + uint8_t type[16]; + uint8_t label[16]; + } *pobj; + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + const char *type = dpaa2_ttos(dtype); + int error; + + if (portal == NULL || cmd == NULL || obj == NULL) + return (DPAA2_CMD_STAT_ERR); + + args = (struct get_obj_desc_args *) &cmd->params[0]; + args->obj_id = obj_id; + memcpy(args->type, type, min(strlen(type) + 1, TYPE_LEN_MAX)); + + error = dpaa2_rc_exec_cmd(portal, cmd, CMDID_RC_GET_OBJ_DESC); + if (!error) { + pobj = (struct dpaa2_obj_resp *) &cmd->params[0]; + obj->id = pobj->id; + obj->vendor = pobj->vendor; + obj->irq_count = pobj->irq_count; + obj->reg_count = pobj->reg_count; + obj->state = pobj->state; + obj->ver_major = pobj->ver_major; + obj->ver_minor = pobj->ver_minor; + obj->flags = pobj->flags; + obj->type = dpaa2_stot((const char *) pobj->type); + memcpy(obj->label, pobj->label, sizeof(pobj->label)); + } + + /* Some DPAA2 objects might not be supported by the driver yet. */ + if (obj->type == DPAA2_DEV_NOTYPE) + error = DPAA2_CMD_STAT_UNKNOWN_OBJ; + + return (error); +} + +static int +dpaa2_rc_get_attributes(device_t dev, device_t child, struct dpaa2_cmd *cmd, + struct dpaa2_rc_attr *attr) +{ + struct __packed dpaa2_rc_attr { + uint32_t cont_id; + uint32_t icid; + uint32_t options; + uint32_t portal_id; + } *pattr; + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + int error; + + if (portal == NULL || cmd == NULL || attr == NULL) + return (DPAA2_CMD_STAT_ERR); + + error = dpaa2_rc_exec_cmd(portal, cmd, CMDID_RC_GET_ATTR); + if (!error) { + pattr = (struct dpaa2_rc_attr *) &cmd->params[0]; + attr->cont_id = pattr->cont_id; + attr->portal_id = pattr->portal_id; + attr->options = pattr->options; + attr->icid = pattr->icid; + } + + return (error); +} + +static int +dpaa2_rc_get_obj_region(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint32_t obj_id, uint8_t reg_idx, enum dpaa2_dev_type dtype, + struct dpaa2_rc_obj_region *reg) +{ + struct __packed obj_region_args { + uint32_t obj_id; + uint16_t _reserved1; + uint8_t reg_idx; + uint8_t _reserved2; + uint64_t _reserved3; + uint64_t _reserved4; + uint8_t type[16]; + } *args; + struct __packed obj_region { + uint64_t _reserved1; + uint64_t base_offset; + uint32_t size; + uint32_t type; + uint32_t flags; + uint32_t _reserved2; + uint64_t base_paddr; + } *resp; + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + uint16_t cmdid, api_major, api_minor; + const char *type = dpaa2_ttos(dtype); + int error; + + if (portal == NULL || cmd == NULL || reg == NULL) + return (DPAA2_CMD_STAT_ERR); + + /* + * If the DPRC object version was not yet cached, cache it now. + * Otherwise use the already cached value. + */ + if (!portal->rc_api_major && !portal->rc_api_minor) { + error = DPAA2_CMD_RC_GET_API_VERSION(dev, child, cmd, + &api_major, &api_minor); + if (error) + return (error); + portal->rc_api_major = api_major; + portal->rc_api_minor = api_minor; + } else { + api_major = portal->rc_api_major; + api_minor = portal->rc_api_minor; + } + + /* TODO: Remove magic numbers. */ + if (api_major > 6u || (api_major == 6u && api_minor >= 6u)) + /* + * MC API version 6.6 changed the size of the MC portals and + * software portals to 64K (as implemented by hardware). + */ + cmdid = CMDID_RC_GET_OBJ_REG_V3; + else if (api_major == 6u && api_minor >= 3u) + /* + * MC API version 6.3 introduced a new field to the region + * descriptor: base_address. + */ + cmdid = CMDID_RC_GET_OBJ_REG_V2; + else + cmdid = CMDID_RC_GET_OBJ_REG; + + args = (struct obj_region_args *) &cmd->params[0]; + args->obj_id = obj_id; + args->reg_idx = reg_idx; + memcpy(args->type, type, min(strlen(type) + 1, TYPE_LEN_MAX)); + + error = dpaa2_rc_exec_cmd(portal, cmd, cmdid); + if (!error) { + resp = (struct obj_region *) &cmd->params[0]; + reg->base_paddr = resp->base_paddr; + reg->base_offset = resp->base_offset; + reg->size = resp->size; + reg->flags = resp->flags; + reg->type = resp->type & 0xFu; + } + + return (error); +} + +static int +dpaa2_rc_get_api_version(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint16_t *major, uint16_t *minor) +{ + struct __packed rc_api_version { + uint16_t major; + uint16_t minor; + } *resp; + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + int error; + + if (portal == NULL || cmd == NULL || major == NULL || minor == NULL) + return (DPAA2_CMD_STAT_ERR); + + error = dpaa2_rc_exec_cmd(portal, cmd, CMDID_RC_GET_API_VERSION); + if (!error) { + resp = (struct rc_api_version *) &cmd->params[0]; + *major = resp->major; + *minor = resp->minor; + } + + return (error); +} + +static int +dpaa2_rc_set_irq_enable(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint8_t irq_idx, uint8_t enable) +{ + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + + if (portal == NULL || cmd == NULL) + return (DPAA2_CMD_STAT_ERR); + + return (dpaa2_rc_enable_irq(portal, cmd, irq_idx, enable, + CMDID_RC_SET_IRQ_ENABLE)); +} + +static int +dpaa2_rc_set_obj_irq(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint8_t irq_idx, uint64_t addr, uint32_t data, uint32_t irq_usr, + uint32_t obj_id, enum dpaa2_dev_type dtype) +{ + struct __packed set_obj_irq_args { + uint32_t data; + uint8_t irq_idx; + uint8_t _reserved1[3]; + uint64_t addr; + uint32_t irq_usr; + uint32_t obj_id; + uint8_t type[16]; + } *args; + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + const char *type = dpaa2_ttos(dtype); + + if (portal == NULL || cmd == NULL) + return (DPAA2_CMD_STAT_ERR); + + args = (struct set_obj_irq_args *) &cmd->params[0]; + args->irq_idx = irq_idx; + args->addr = addr; + args->data = data; + args->irq_usr = irq_usr; + args->obj_id = obj_id; + memcpy(args->type, type, min(strlen(type) + 1, TYPE_LEN_MAX)); + + return (dpaa2_rc_exec_cmd(portal, cmd, CMDID_RC_SET_OBJ_IRQ)); +} + +static int +dpaa2_rc_get_conn(device_t dev, device_t child, struct dpaa2_cmd *cmd, + struct dpaa2_ep_desc *ep1_desc, struct dpaa2_ep_desc *ep2_desc, + uint32_t *link_stat) +{ + struct __packed get_conn_args { + uint32_t ep1_id; + uint32_t ep1_ifid; + uint8_t ep1_type[16]; + uint64_t _reserved[4]; + } *args; + struct __packed get_conn_resp { + uint64_t _reserved1[3]; + uint32_t ep2_id; + uint32_t ep2_ifid; + uint8_t ep2_type[16]; + uint32_t link_stat; + uint32_t _reserved2; + } *resp; + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + int error; + + if (portal == NULL || cmd == NULL || ep1_desc == NULL || + ep2_desc == NULL) + return (DPAA2_CMD_STAT_ERR); + + args = (struct get_conn_args *) &cmd->params[0]; + args->ep1_id = ep1_desc->obj_id; + args->ep1_ifid = ep1_desc->if_id; + /* TODO: Remove magic number. */ + strncpy(args->ep1_type, dpaa2_ttos(ep1_desc->type), 16); + + error = dpaa2_rc_exec_cmd(portal, cmd, CMDID_RC_GET_CONN); + if (!error) { + resp = (struct get_conn_resp *) &cmd->params[0]; + ep2_desc->obj_id = resp->ep2_id; + ep2_desc->if_id = resp->ep2_ifid; + ep2_desc->type = dpaa2_stot((const char *) resp->ep2_type); + if (link_stat != NULL) + *link_stat = resp->link_stat; + } + + return (error); +} + +static int +dpaa2_rc_ni_open(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint32_t dpni_id, uint16_t *token) +{ + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + struct dpaa2_cmd_header *hdr; + int error; + + if (portal == NULL || cmd == NULL || token == NULL) + return (DPAA2_CMD_STAT_ERR); + + cmd->params[0] = dpni_id; + error = dpaa2_rc_exec_cmd(portal, cmd, CMDID_NI_OPEN); + if (!error) { + hdr = (struct dpaa2_cmd_header *) &cmd->header; + *token = hdr->token; + } + + return (error); +} + +static int +dpaa2_rc_ni_close(device_t dev, device_t child, struct dpaa2_cmd *cmd) +{ + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + + if (portal == NULL || cmd == NULL) + return (DPAA2_CMD_STAT_ERR); + + return (dpaa2_rc_exec_cmd(portal, cmd, CMDID_NI_CLOSE)); +} + +static int +dpaa2_rc_ni_enable(device_t dev, device_t child, struct dpaa2_cmd *cmd) +{ + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + + if (portal == NULL || cmd == NULL) + return (DPAA2_CMD_STAT_ERR); + + return (dpaa2_rc_exec_cmd(portal, cmd, CMDID_NI_ENABLE)); +} + +static int +dpaa2_rc_ni_disable(device_t dev, device_t child, struct dpaa2_cmd *cmd) +{ + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + + if (portal == NULL || cmd == NULL) + return (DPAA2_CMD_STAT_ERR); + + return (dpaa2_rc_exec_cmd(portal, cmd, CMDID_NI_DISABLE)); +} + +static int +dpaa2_rc_ni_get_api_version(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint16_t *major, uint16_t *minor) +{ + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + int error; + + if (portal == NULL || cmd == NULL || major == NULL || minor == NULL) + return (DPAA2_CMD_STAT_ERR); + + error = dpaa2_rc_exec_cmd(portal, cmd, CMDID_NI_GET_API_VER); + if (!error) { + *major = cmd->params[0] & 0xFFFFU; + *minor = (cmd->params[0] >> 16) & 0xFFFFU; + } + + return (error); +} + +static int +dpaa2_rc_ni_reset(device_t dev, device_t child, struct dpaa2_cmd *cmd) +{ + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + + if (portal == NULL || cmd == NULL) + return (DPAA2_CMD_STAT_ERR); + + return (dpaa2_rc_exec_cmd(portal, cmd, CMDID_NI_RESET)); +} + +static int +dpaa2_rc_ni_get_attributes(device_t dev, device_t child, struct dpaa2_cmd *cmd, + struct dpaa2_ni_attr *attr) +{ + struct __packed ni_attr { + uint32_t options; + uint8_t num_queues; + uint8_t num_rx_tcs; + uint8_t mac_entries; + uint8_t num_tx_tcs; + uint8_t vlan_entries; + uint8_t num_channels; + uint8_t qos_entries; + uint8_t _reserved1; + uint16_t fs_entries; + uint16_t _reserved2; + uint8_t qos_key_size; + uint8_t fs_key_size; + uint16_t wriop_ver; + uint8_t num_cgs; + uint8_t _reserved3; + uint16_t _reserved4; + uint64_t _reserved5[4]; + } *resp; + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + int error; + + if (portal == NULL || cmd == NULL || attr == NULL) + return (DPAA2_CMD_STAT_ERR); + + error = dpaa2_rc_exec_cmd(portal, cmd, CMDID_NI_GET_ATTR); + if (!error) { + resp = (struct ni_attr *) &cmd->params[0]; + + attr->options = resp->options; + attr->wriop_ver = resp->wriop_ver; + + attr->entries.fs = resp->fs_entries; + attr->entries.mac = resp->mac_entries; + attr->entries.vlan = resp->vlan_entries; + attr->entries.qos = resp->qos_entries; + + attr->num.queues = resp->num_queues; + attr->num.rx_tcs = resp->num_rx_tcs; + attr->num.tx_tcs = resp->num_tx_tcs; + attr->num.channels = resp->num_channels; + attr->num.cgs = resp->num_cgs; + + attr->key_size.fs = resp->fs_key_size; + attr->key_size.qos = resp->qos_key_size; + } + + return (error); +} + +static int +dpaa2_rc_ni_set_buf_layout(device_t dev, device_t child, struct dpaa2_cmd *cmd, + struct dpaa2_ni_buf_layout *bl) +{ + struct __packed set_buf_layout_args { + uint8_t queue_type; + uint8_t _reserved1; + uint16_t _reserved2; + uint16_t options; + uint8_t params; + uint8_t _reserved3; + uint16_t priv_data_size; + uint16_t data_align; + uint16_t head_room; + uint16_t tail_room; + uint64_t _reserved4[5]; + } *args; + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + + if (portal == NULL || cmd == NULL || bl == NULL) + return (DPAA2_CMD_STAT_ERR); + + args = (struct set_buf_layout_args *) &cmd->params[0]; + args->queue_type = (uint8_t) bl->queue_type; + args->options = bl->options; + args->params = 0; + args->priv_data_size = bl->pd_size; + args->data_align = bl->fd_align; + args->head_room = bl->head_size; + args->tail_room = bl->tail_size; + + args->params |= bl->pass_timestamp ? 1U : 0U; + args->params |= bl->pass_parser_result ? 2U : 0U; + args->params |= bl->pass_frame_status ? 4U : 0U; + args->params |= bl->pass_sw_opaque ? 8U : 0U; + + return (dpaa2_rc_exec_cmd(portal, cmd, CMDID_NI_SET_BUF_LAYOUT)); +} + +static int +dpaa2_rc_ni_get_tx_data_offset(device_t dev, device_t child, + struct dpaa2_cmd *cmd, uint16_t *offset) +{ + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + int error; + + if (portal == NULL || cmd == NULL || offset == NULL) + return (DPAA2_CMD_STAT_ERR); + + error = dpaa2_rc_exec_cmd(portal, cmd, CMDID_NI_GET_TX_DATA_OFF); + if (!error) + *offset = cmd->params[0] & 0xFFFFU; + + return (error); +} + +static int +dpaa2_rc_ni_get_port_mac_addr(device_t dev, device_t child, + struct dpaa2_cmd *cmd, uint8_t *mac) +{ + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + int error; + + if (portal == NULL || cmd == NULL || mac == NULL) + return (DPAA2_CMD_STAT_ERR); + + error = dpaa2_rc_exec_cmd(portal, cmd, CMDID_NI_GET_PORT_MAC_ADDR); + if (!error) { + mac[0] = (cmd->params[0] >> 56) & 0xFFU; + mac[1] = (cmd->params[0] >> 48) & 0xFFU; + mac[2] = (cmd->params[0] >> 40) & 0xFFU; + mac[3] = (cmd->params[0] >> 32) & 0xFFU; + mac[4] = (cmd->params[0] >> 24) & 0xFFU; + mac[5] = (cmd->params[0] >> 16) & 0xFFU; + } + + return (error); +} + +static int +dpaa2_rc_ni_set_prim_mac_addr(device_t dev, device_t child, + struct dpaa2_cmd *cmd, uint8_t *mac) +{ + struct __packed set_prim_mac_args { + uint8_t _reserved[2]; + uint8_t mac[ETHER_ADDR_LEN]; + } *args; + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + + if (portal == NULL || cmd == NULL || mac == NULL) + return (DPAA2_CMD_STAT_EINVAL); + + args = (struct set_prim_mac_args *) &cmd->params[0]; + for (int i = 1; i <= ETHER_ADDR_LEN; i++) + args->mac[i - 1] = mac[ETHER_ADDR_LEN - i]; + + return (dpaa2_rc_exec_cmd(portal, cmd, CMDID_NI_SET_PRIM_MAC_ADDR)); +} + +static int +dpaa2_rc_ni_get_prim_mac_addr(device_t dev, device_t child, + struct dpaa2_cmd *cmd, uint8_t *mac) +{ + struct __packed get_prim_mac_resp { + uint8_t _reserved[2]; + uint8_t mac[ETHER_ADDR_LEN]; + } *resp; + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + int error; + + if (portal == NULL || cmd == NULL || mac == NULL) + return (DPAA2_CMD_STAT_EINVAL); + + error = dpaa2_rc_exec_cmd(portal, cmd, CMDID_NI_GET_PRIM_MAC_ADDR); + if (!error) { + resp = (struct get_prim_mac_resp *) &cmd->params[0]; + for (int i = 1; i <= ETHER_ADDR_LEN; i++) + mac[ETHER_ADDR_LEN - i] = resp->mac[i - 1]; + } + + return (error); +} + +static int +dpaa2_rc_ni_set_link_cfg(device_t dev, device_t child, struct dpaa2_cmd *cmd, + struct dpaa2_ni_link_cfg *cfg) +{ + struct __packed link_cfg_args { + uint64_t _reserved1; + uint32_t rate; + uint32_t _reserved2; + uint64_t options; + uint64_t adv_speeds; + uint64_t _reserved3[3]; + } *args; + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + + if (portal == NULL || cmd == NULL || cfg == NULL) + return (DPAA2_CMD_STAT_EINVAL); + + args = (struct link_cfg_args *) &cmd->params[0]; + args->rate = cfg->rate; + args->options = cfg->options; + args->adv_speeds = cfg->adv_speeds; + + return (dpaa2_rc_exec_cmd(portal, cmd, CMDID_NI_SET_LINK_CFG)); +} + +static int +dpaa2_rc_ni_get_link_cfg(device_t dev, device_t child, struct dpaa2_cmd *cmd, + struct dpaa2_ni_link_cfg *cfg) +{ + struct __packed link_cfg_resp { + uint64_t _reserved1; + uint32_t rate; + uint32_t _reserved2; + uint64_t options; + uint64_t adv_speeds; + uint64_t _reserved3[3]; + } *resp; + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + int error; + + if (portal == NULL || cmd == NULL || cfg == NULL) + return (DPAA2_CMD_STAT_EINVAL); + + error = dpaa2_rc_exec_cmd(portal, cmd, CMDID_NI_GET_LINK_CFG); + if (!error) { + resp = (struct link_cfg_resp *) &cmd->params[0]; + cfg->rate = resp->rate; + cfg->options = resp->options; + cfg->adv_speeds = resp->adv_speeds; + } + + return (error); +} + +static int +dpaa2_rc_ni_get_link_state(device_t dev, device_t child, struct dpaa2_cmd *cmd, + struct dpaa2_ni_link_state *state) +{ + struct __packed link_state_resp { + uint32_t _reserved1; + uint32_t flags; + uint32_t rate; + uint32_t _reserved2; + uint64_t options; + uint64_t supported; + uint64_t advert; + } *resp; + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + int error; + + if (portal == NULL || cmd == NULL || state == NULL) + return (DPAA2_CMD_STAT_EINVAL); + + dpaa2_rc_reset_cmd_params(cmd); + + error = dpaa2_rc_exec_cmd(portal, cmd, CMDID_NI_GET_LINK_STATE); + if (!error) { + resp = (struct link_state_resp *) &cmd->params[0]; + state->options = resp->options; + state->adv_speeds = resp->advert; + state->sup_speeds = resp->supported; + state->rate = resp->rate; + + state->link_up = resp->flags & 0x1u ? true : false; + state->state_valid = resp->flags & 0x2u ? true : false; + } + + return (error); +} + +static int +dpaa2_rc_ni_set_qos_table(device_t dev, device_t child, struct dpaa2_cmd *cmd, + struct dpaa2_ni_qos_table *tbl) +{ + struct __packed qos_table_args { + uint32_t _reserved1; + uint8_t default_tc; + uint8_t options; + uint16_t _reserved2; + uint64_t _reserved[5]; + uint64_t kcfg_busaddr; + } *args; + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + + if (portal == NULL || cmd == NULL || tbl == NULL) + return (DPAA2_CMD_STAT_EINVAL); + + dpaa2_rc_reset_cmd_params(cmd); + + args = (struct qos_table_args *) &cmd->params[0]; + args->default_tc = tbl->default_tc; + args->kcfg_busaddr = tbl->kcfg_busaddr; + + args->options |= tbl->discard_on_miss ? 1U : 0U; + args->options |= tbl->keep_entries ? 2U : 0U; + + return (dpaa2_rc_exec_cmd(portal, cmd, CMDID_NI_SET_QOS_TABLE)); +} + +static int +dpaa2_rc_ni_clear_qos_table(device_t dev, device_t child, struct dpaa2_cmd *cmd) +{ + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + + if (portal == NULL || cmd == NULL) + return (DPAA2_CMD_STAT_EINVAL); + + return (dpaa2_rc_exec_cmd(portal, cmd, CMDID_NI_CLEAR_QOS_TABLE)); +} + +static int +dpaa2_rc_ni_set_pools(device_t dev, device_t child, struct dpaa2_cmd *cmd, + struct dpaa2_ni_pools_cfg *cfg) +{ + struct __packed set_pools_args { + uint8_t pools_num; + uint8_t backup_pool_mask; + uint8_t _reserved1; + uint8_t pool_as; /* assigning: 0 - QPRI, 1 - QDBIN */ + uint32_t bp_obj_id[DPAA2_NI_MAX_POOLS]; + uint16_t buf_sz[DPAA2_NI_MAX_POOLS]; + uint32_t _reserved2; + } *args; + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + + if (portal == NULL || cmd == NULL || cfg == NULL) + return (DPAA2_CMD_STAT_EINVAL); + + dpaa2_rc_reset_cmd_params(cmd); + + args = (struct set_pools_args *) &cmd->params[0]; + args->pools_num = cfg->pools_num < DPAA2_NI_MAX_POOLS + ? cfg->pools_num : DPAA2_NI_MAX_POOLS; + for (uint32_t i = 0; i < args->pools_num; i++) { + args->bp_obj_id[i] = cfg->pools[i].bp_obj_id; + args->buf_sz[i] = cfg->pools[i].buf_sz; + args->backup_pool_mask |= (cfg->pools[i].backup_flag & 1) << i; + } + + return (dpaa2_rc_exec_cmd(portal, cmd, CMDID_NI_SET_POOLS)); +} + +static int +dpaa2_rc_ni_set_err_behavior(device_t dev, device_t child, struct dpaa2_cmd *cmd, + struct dpaa2_ni_err_cfg *cfg) +{ + struct __packed err_behavior_args { + uint32_t err_mask; + uint8_t flags; + } *args; + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + + if (portal == NULL || cmd == NULL || cfg == NULL) + return (DPAA2_CMD_STAT_EINVAL); + + dpaa2_rc_reset_cmd_params(cmd); + + args = (struct err_behavior_args *) &cmd->params[0]; + args->err_mask = cfg->err_mask; + + args->flags |= cfg->set_err_fas ? 0x10u : 0u; + args->flags |= ((uint8_t) cfg->action) & 0x0Fu; + + return (dpaa2_rc_exec_cmd(portal, cmd, CMDID_NI_SET_ERR_BEHAVIOR)); +} + +static int +dpaa2_rc_ni_get_queue(device_t dev, device_t child, struct dpaa2_cmd *cmd, + struct dpaa2_ni_queue_cfg *cfg) +{ + struct __packed get_queue_args { + uint8_t queue_type; + uint8_t tc; + uint8_t idx; + uint8_t chan_id; + } *args; + struct __packed get_queue_resp { + uint64_t _reserved1; + uint32_t dest_id; + uint16_t _reserved2; + uint8_t priority; + uint8_t flags; + uint64_t flc; + uint64_t user_ctx; + uint32_t fqid; + uint16_t qdbin; + uint16_t _reserved3; + uint8_t cgid; + uint8_t _reserved[15]; + } *resp; + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + int error; + + if (portal == NULL || cmd == NULL || cfg == NULL) + return (DPAA2_CMD_STAT_EINVAL); + + dpaa2_rc_reset_cmd_params(cmd); + + args = (struct get_queue_args *) &cmd->params[0]; + args->queue_type = (uint8_t) cfg->type; + args->tc = cfg->tc; + args->idx = cfg->idx; + args->chan_id = cfg->chan_id; + + error = dpaa2_rc_exec_cmd(portal, cmd, CMDID_NI_GET_QUEUE); + if (!error) { + resp = (struct get_queue_resp *) &cmd->params[0]; + + cfg->dest_id = resp->dest_id; + cfg->priority = resp->priority; + cfg->flow_ctx = resp->flc; + cfg->user_ctx = resp->user_ctx; + cfg->fqid = resp->fqid; + cfg->qdbin = resp->qdbin; + cfg->cgid = resp->cgid; + + cfg->dest_type = (enum dpaa2_ni_dest_type) resp->flags & 0x0Fu; + cfg->cgid_valid = (resp->flags & 0x20u) > 0u ? true : false; + cfg->stash_control = (resp->flags & 0x40u) > 0u ? true : false; + cfg->hold_active = (resp->flags & 0x80u) > 0u ? true : false; + } + + return (error); +} + +static int +dpaa2_rc_ni_set_queue(device_t dev, device_t child, struct dpaa2_cmd *cmd, + struct dpaa2_ni_queue_cfg *cfg) +{ + struct __packed set_queue_args { + uint8_t queue_type; + uint8_t tc; + uint8_t idx; + uint8_t options; + uint32_t _reserved1; + uint32_t dest_id; + uint16_t _reserved2; + uint8_t priority; + uint8_t flags; + uint64_t flc; + uint64_t user_ctx; + uint8_t cgid; + uint8_t chan_id; + uint8_t _reserved[23]; + } *args; + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + + if (portal == NULL || cmd == NULL || cfg == NULL) + return (DPAA2_CMD_STAT_EINVAL); + + dpaa2_rc_reset_cmd_params(cmd); + + args = (struct set_queue_args *) &cmd->params[0]; + args->queue_type = (uint8_t) cfg->type; + args->tc = cfg->tc; + args->idx = cfg->idx; + args->options = cfg->options; + args->dest_id = cfg->dest_id; + args->priority = cfg->priority; + args->flc = cfg->flow_ctx; + args->user_ctx = cfg->user_ctx; + args->cgid = cfg->cgid; + args->chan_id = cfg->chan_id; + + args->flags |= (uint8_t)(cfg->dest_type & 0x0Fu); + args->flags |= cfg->stash_control ? 0x40u : 0u; + args->flags |= cfg->hold_active ? 0x80u : 0u; + + return (dpaa2_rc_exec_cmd(portal, cmd, CMDID_NI_SET_QUEUE)); +} + +static int +dpaa2_rc_ni_get_qdid(device_t dev, device_t child, struct dpaa2_cmd *cmd, + enum dpaa2_ni_queue_type type, uint16_t *qdid) +{ + struct __packed get_qdid_args { + uint8_t queue_type; + } *args; + struct __packed get_qdid_resp { + uint16_t qdid; + } *resp; + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + int error; + + if (portal == NULL || cmd == NULL || qdid == NULL) + return (DPAA2_CMD_STAT_EINVAL); + + dpaa2_rc_reset_cmd_params(cmd); + + args = (struct get_qdid_args *) &cmd->params[0]; + args->queue_type = (uint8_t) type; + + error = dpaa2_rc_exec_cmd(portal, cmd, CMDID_NI_GET_QDID); + if (!error) { + resp = (struct get_qdid_resp *) &cmd->params[0]; + *qdid = resp->qdid; + } + + return (error); +} + +static int +dpaa2_rc_ni_add_mac_addr(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint8_t *mac) +{ + struct __packed add_mac_args { + uint8_t flags; + uint8_t _reserved; + uint8_t mac[ETHER_ADDR_LEN]; + uint8_t tc_id; + uint8_t fq_id; + } *args; + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + + if (portal == NULL || cmd == NULL || mac == NULL) + return (DPAA2_CMD_STAT_EINVAL); + + dpaa2_rc_reset_cmd_params(cmd); + + args = (struct add_mac_args *) &cmd->params[0]; + for (int i = 1; i <= ETHER_ADDR_LEN; i++) + args->mac[i - 1] = mac[ETHER_ADDR_LEN - i]; + + return (dpaa2_rc_exec_cmd(portal, cmd, CMDID_NI_ADD_MAC_ADDR)); +} + +static int +dpaa2_rc_ni_remove_mac_addr(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint8_t *mac) +{ + struct __packed rem_mac_args { + uint16_t _reserved; + uint8_t mac[ETHER_ADDR_LEN]; + uint64_t _reserved1[6]; + } *args; + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + + if (portal == NULL || cmd == NULL || mac == NULL) + return (DPAA2_CMD_STAT_EINVAL); + + dpaa2_rc_reset_cmd_params(cmd); + + args = (struct rem_mac_args *) &cmd->params[0]; + for (int i = 1; i <= ETHER_ADDR_LEN; i++) + args->mac[i - 1] = mac[ETHER_ADDR_LEN - i]; + + return (dpaa2_rc_exec_cmd(portal, cmd, CMDID_NI_REMOVE_MAC_ADDR)); +} + +static int +dpaa2_rc_ni_clear_mac_filters(device_t dev, device_t child, + struct dpaa2_cmd *cmd, bool rm_uni, bool rm_multi) +{ + struct __packed clear_mac_filters_args { + uint8_t flags; + } *args; + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + + if (portal == NULL || cmd == NULL) + return (DPAA2_CMD_STAT_EINVAL); + + dpaa2_rc_reset_cmd_params(cmd); + + args = (struct clear_mac_filters_args *) &cmd->params[0]; + args->flags |= rm_uni ? 0x1 : 0x0; + args->flags |= rm_multi ? 0x2 : 0x0; + + return (dpaa2_rc_exec_cmd(portal, cmd, CMDID_NI_CLEAR_MAC_FILTERS)); +} + +static int +dpaa2_rc_ni_set_mfl(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint16_t length) +{ + struct __packed set_mfl_args { + uint16_t length; + } *args; + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + + if (portal == NULL || cmd == NULL) + return (DPAA2_CMD_STAT_EINVAL); + + dpaa2_rc_reset_cmd_params(cmd); + + args = (struct set_mfl_args *) &cmd->params[0]; + args->length = length; + + return (dpaa2_rc_exec_cmd(portal, cmd, CMDID_NI_SET_MFL)); +} + +static int +dpaa2_rc_ni_set_offload(device_t dev, device_t child, struct dpaa2_cmd *cmd, + enum dpaa2_ni_ofl_type ofl_type, bool en) +{ + struct __packed set_ofl_args { + uint8_t _reserved[3]; + uint8_t ofl_type; + uint32_t config; + } *args; + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + + if (portal == NULL || cmd == NULL) + return (DPAA2_CMD_STAT_EINVAL); + + dpaa2_rc_reset_cmd_params(cmd); + + args = (struct set_ofl_args *) &cmd->params[0]; + args->ofl_type = (uint8_t) ofl_type; + args->config = en ? 1u : 0u; + + return (dpaa2_rc_exec_cmd(portal, cmd, CMDID_NI_SET_OFFLOAD)); +} + +static int +dpaa2_rc_ni_set_irq_mask(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint8_t irq_idx, uint32_t mask) +{ + struct __packed set_irq_mask_args { + uint32_t mask; + uint8_t irq_idx; + } *args; + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + + if (portal == NULL || cmd == NULL) + return (DPAA2_CMD_STAT_EINVAL); + + dpaa2_rc_reset_cmd_params(cmd); + + args = (struct set_irq_mask_args *) &cmd->params[0]; + args->mask = mask; + args->irq_idx = irq_idx; + + return (dpaa2_rc_exec_cmd(portal, cmd, CMDID_NI_SET_IRQ_MASK)); +} + +static int +dpaa2_rc_ni_set_irq_enable(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint8_t irq_idx, bool en) +{ + struct __packed set_irq_enable_args { + uint32_t en; + uint8_t irq_idx; + } *args; + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + + if (portal == NULL || cmd == NULL) + return (DPAA2_CMD_STAT_EINVAL); + + dpaa2_rc_reset_cmd_params(cmd); + + args = (struct set_irq_enable_args *) &cmd->params[0]; + args->en = en ? 1u : 0u; + args->irq_idx = irq_idx; + + return (dpaa2_rc_exec_cmd(portal, cmd, CMDID_NI_SET_IRQ_ENABLE)); +} + +static int +dpaa2_rc_ni_get_irq_status(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint8_t irq_idx, uint32_t *status) +{ + struct __packed get_irq_stat_args { + uint32_t status; + uint8_t irq_idx; + } *args; + struct __packed get_irq_stat_resp { + uint32_t status; + } *resp; + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + int error; + + if (portal == NULL || cmd == NULL || status == NULL) + return (DPAA2_CMD_STAT_EINVAL); + + dpaa2_rc_reset_cmd_params(cmd); + + args = (struct get_irq_stat_args *) &cmd->params[0]; + args->status = *status; + args->irq_idx = irq_idx; + + error = dpaa2_rc_exec_cmd(portal, cmd, CMDID_NI_GET_IRQ_STATUS); + if (!error) { + resp = (struct get_irq_stat_resp *) &cmd->params[0]; + *status = resp->status; + } + + return (error); +} + +static int +dpaa2_rc_ni_set_uni_promisc(device_t dev, device_t child, struct dpaa2_cmd *cmd, + bool en) +{ + struct __packed set_uni_promisc_args { + uint8_t en; + } *args; + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + + if (portal == NULL || cmd == NULL) + return (DPAA2_CMD_STAT_EINVAL); + + dpaa2_rc_reset_cmd_params(cmd); + + args = (struct set_uni_promisc_args *) &cmd->params[0]; + args->en = en ? 1u : 0u; + + return (dpaa2_rc_exec_cmd(portal, cmd, CMDID_NI_SET_UNI_PROMISC)); +} + +static int +dpaa2_rc_ni_set_multi_promisc(device_t dev, device_t child, + struct dpaa2_cmd *cmd, bool en) +{ + /* TODO: Implementation is the same as for ni_set_uni_promisc(). */ + struct __packed set_multi_promisc_args { + uint8_t en; + } *args; + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + + if (portal == NULL || cmd == NULL) + return (DPAA2_CMD_STAT_EINVAL); + + dpaa2_rc_reset_cmd_params(cmd); + + args = (struct set_multi_promisc_args *) &cmd->params[0]; + args->en = en ? 1u : 0u; + + return (dpaa2_rc_exec_cmd(portal, cmd, CMDID_NI_SET_MULTI_PROMISC)); +} + +static int +dpaa2_rc_ni_get_statistics(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint8_t page, uint16_t param, uint64_t *cnt) +{ + struct __packed get_statistics_args { + uint8_t page; + uint16_t param; + } *args; + struct __packed get_statistics_resp { + uint64_t cnt[7]; + } *resp; + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + int error; + + if (portal == NULL || cmd == NULL || cnt == NULL) + return (DPAA2_CMD_STAT_EINVAL); + + dpaa2_rc_reset_cmd_params(cmd); + + args = (struct get_statistics_args *) &cmd->params[0]; + args->page = page; + args->param = param; + + error = dpaa2_rc_exec_cmd(portal, cmd, CMDID_NI_GET_STATISTICS); + if (!error) { + resp = (struct get_statistics_resp *) &cmd->params[0]; + for (int i = 0; i < DPAA2_NI_STAT_COUNTERS; i++) + cnt[i] = resp->cnt[i]; + } + + return (error); +} + +static int +dpaa2_rc_ni_set_rx_tc_dist(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint16_t dist_size, uint8_t tc, enum dpaa2_ni_dist_mode dist_mode, + bus_addr_t key_cfg_buf) +{ + struct __packed set_rx_tc_dist_args { + uint16_t dist_size; + uint8_t tc; + uint8_t ma_dm; /* miss action + dist. mode */ + uint32_t _reserved1; + uint64_t _reserved2[5]; + uint64_t key_cfg_iova; + } *args; + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + + if (portal == NULL || cmd == NULL) + return (DPAA2_CMD_STAT_EINVAL); + + dpaa2_rc_reset_cmd_params(cmd); + + args = (struct set_rx_tc_dist_args *) &cmd->params[0]; + args->dist_size = dist_size; + args->tc = tc; + args->ma_dm = ((uint8_t) dist_mode) & 0x0Fu; + args->key_cfg_iova = key_cfg_buf; + + return (dpaa2_rc_exec_cmd(portal, cmd, CMDID_NI_SET_RX_TC_DIST)); +} + +static int +dpaa2_rc_io_open(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint32_t dpio_id, uint16_t *token) +{ + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + struct dpaa2_cmd_header *hdr; + int error; + + if (portal == NULL || cmd == NULL || token == NULL) + return (DPAA2_CMD_STAT_ERR); + + cmd->params[0] = dpio_id; + error = dpaa2_rc_exec_cmd(portal, cmd, CMDID_IO_OPEN); + if (!error) { + hdr = (struct dpaa2_cmd_header *) &cmd->header; + *token = hdr->token; + } + + return (error); +} + +static int +dpaa2_rc_io_close(device_t dev, device_t child, struct dpaa2_cmd *cmd) +{ + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + + if (portal == NULL || cmd == NULL) + return (DPAA2_CMD_STAT_ERR); + + return (dpaa2_rc_exec_cmd(portal, cmd, CMDID_IO_CLOSE)); +} + +static int +dpaa2_rc_io_enable(device_t dev, device_t child, struct dpaa2_cmd *cmd) +{ + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + + if (portal == NULL || cmd == NULL) + return (DPAA2_CMD_STAT_ERR); + + return (dpaa2_rc_exec_cmd(portal, cmd, CMDID_IO_ENABLE)); +} + +static int +dpaa2_rc_io_disable(device_t dev, device_t child, struct dpaa2_cmd *cmd) +{ + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + + if (portal == NULL || cmd == NULL) + return (DPAA2_CMD_STAT_ERR); + + return (dpaa2_rc_exec_cmd(portal, cmd, CMDID_IO_DISABLE)); +} + +static int +dpaa2_rc_io_reset(device_t dev, device_t child, struct dpaa2_cmd *cmd) +{ + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + + if (portal == NULL || cmd == NULL) + return (DPAA2_CMD_STAT_ERR); + + return (dpaa2_rc_exec_cmd(portal, cmd, CMDID_IO_RESET)); +} + +static int +dpaa2_rc_io_get_attributes(device_t dev, device_t child, struct dpaa2_cmd *cmd, + struct dpaa2_io_attr *attr) +{ + struct __packed dpaa2_io_attr { + uint32_t id; + uint16_t swp_id; + uint8_t priors_num; + uint8_t chan_mode; + uint64_t swp_ce_paddr; + uint64_t swp_ci_paddr; + uint32_t swp_version; + uint32_t _reserved1; + uint32_t swp_clk; + uint32_t _reserved2[5]; + } *pattr; + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + int error; + + if (portal == NULL || cmd == NULL || attr == NULL) + return (DPAA2_CMD_STAT_ERR); + + error = dpaa2_rc_exec_cmd(portal, cmd, CMDID_IO_GET_ATTR); + if (!error) { + pattr = (struct dpaa2_io_attr *) &cmd->params[0]; + + attr->swp_ce_paddr = pattr->swp_ce_paddr; + attr->swp_ci_paddr = pattr->swp_ci_paddr; + attr->swp_version = pattr->swp_version; + attr->swp_clk = pattr->swp_clk; + attr->id = pattr->id; + attr->swp_id = pattr->swp_id; + attr->priors_num = pattr->priors_num; + attr->chan_mode = (enum dpaa2_io_chan_mode) + pattr->chan_mode; + } + + return (error); +} + +static int +dpaa2_rc_io_set_irq_mask(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint8_t irq_idx, uint32_t mask) +{ + /* TODO: Extract similar *_set_irq_mask() into one function. */ + struct __packed set_irq_mask_args { + uint32_t mask; + uint8_t irq_idx; + } *args; + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + + if (portal == NULL || cmd == NULL) + return (DPAA2_CMD_STAT_EINVAL); + + dpaa2_rc_reset_cmd_params(cmd); + + args = (struct set_irq_mask_args *) &cmd->params[0]; + args->mask = mask; + args->irq_idx = irq_idx; + + return (dpaa2_rc_exec_cmd(portal, cmd, CMDID_IO_SET_IRQ_MASK)); +} + +static int +dpaa2_rc_io_get_irq_status(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint8_t irq_idx, uint32_t *status) +{ + /* TODO: Extract similar *_get_irq_status() into one function. */ + struct __packed get_irq_stat_args { + uint32_t status; + uint8_t irq_idx; + } *args; + struct __packed get_irq_stat_resp { + uint32_t status; + } *resp; + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + int error; + + if (portal == NULL || cmd == NULL || status == NULL) + return (DPAA2_CMD_STAT_EINVAL); + + dpaa2_rc_reset_cmd_params(cmd); + + args = (struct get_irq_stat_args *) &cmd->params[0]; + args->status = *status; + args->irq_idx = irq_idx; + + error = dpaa2_rc_exec_cmd(portal, cmd, CMDID_IO_GET_IRQ_STATUS); + if (!error) { + resp = (struct get_irq_stat_resp *) &cmd->params[0]; + *status = resp->status; + } + + return (error); +} + +static int +dpaa2_rc_io_set_irq_enable(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint8_t irq_idx, bool en) +{ + /* TODO: Extract similar *_set_irq_enable() into one function. */ + struct __packed set_irq_enable_args { + uint32_t en; + uint8_t irq_idx; + } *args; + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + + if (portal == NULL || cmd == NULL) + return (DPAA2_CMD_STAT_EINVAL); + + dpaa2_rc_reset_cmd_params(cmd); + + args = (struct set_irq_enable_args *) &cmd->params[0]; + args->en = en ? 1u : 0u; + args->irq_idx = irq_idx; + + return (dpaa2_rc_exec_cmd(portal, cmd, CMDID_IO_SET_IRQ_ENABLE)); +} + +static int +dpaa2_rc_io_add_static_dq_chan(device_t dev, device_t child, + struct dpaa2_cmd *cmd, uint32_t dpcon_id, uint8_t *chan_idx) +{ + struct __packed add_static_dq_chan_args { + uint32_t dpcon_id; + } *args; + struct __packed add_static_dq_chan_resp { + uint8_t chan_idx; + } *resp; + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + int error; + + if (portal == NULL || cmd == NULL || chan_idx == NULL) + return (DPAA2_CMD_STAT_EINVAL); + + dpaa2_rc_reset_cmd_params(cmd); + + args = (struct add_static_dq_chan_args *) &cmd->params[0]; + args->dpcon_id = dpcon_id; + + error = dpaa2_rc_exec_cmd(portal, cmd, CMDID_IO_ADD_STATIC_DQ_CHAN); + if (!error) { + resp = (struct add_static_dq_chan_resp *) &cmd->params[0]; + *chan_idx = resp->chan_idx; + } + + return (error); +} + +static int +dpaa2_rc_bp_open(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint32_t dpbp_id, uint16_t *token) +{ + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + struct dpaa2_cmd_header *hdr; + int error; + + if (portal == NULL || cmd == NULL || token == NULL) + return (DPAA2_CMD_STAT_ERR); + + cmd->params[0] = dpbp_id; + error = dpaa2_rc_exec_cmd(portal, cmd, CMDID_BP_OPEN); + if (!error) { + hdr = (struct dpaa2_cmd_header *) &cmd->header; + *token = hdr->token; + } + + return (error); +} + +static int +dpaa2_rc_bp_close(device_t dev, device_t child, struct dpaa2_cmd *cmd) +{ + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + + if (portal == NULL || cmd == NULL) + return (DPAA2_CMD_STAT_ERR); + + return (dpaa2_rc_exec_cmd(portal, cmd, CMDID_BP_CLOSE)); +} + +static int +dpaa2_rc_bp_enable(device_t dev, device_t child, struct dpaa2_cmd *cmd) +{ + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + + if (portal == NULL || cmd == NULL) + return (DPAA2_CMD_STAT_ERR); + + return (dpaa2_rc_exec_cmd(portal, cmd, CMDID_BP_ENABLE)); +} + +static int +dpaa2_rc_bp_disable(device_t dev, device_t child, struct dpaa2_cmd *cmd) +{ + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + + if (portal == NULL || cmd == NULL) + return (DPAA2_CMD_STAT_ERR); + + return (dpaa2_rc_exec_cmd(portal, cmd, CMDID_BP_DISABLE)); +} + +static int +dpaa2_rc_bp_reset(device_t dev, device_t child, struct dpaa2_cmd *cmd) +{ + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + + if (portal == NULL || cmd == NULL) + return (DPAA2_CMD_STAT_ERR); + + return (dpaa2_rc_exec_cmd(portal, cmd, CMDID_BP_RESET)); +} + +static int +dpaa2_rc_bp_get_attributes(device_t dev, device_t child, struct dpaa2_cmd *cmd, + struct dpaa2_bp_attr *attr) +{ + struct __packed dpaa2_bp_attr { + uint16_t _reserved1; + uint16_t bpid; + uint32_t id; + } *pattr; + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + int error; + + if (portal == NULL || cmd == NULL || attr == NULL) + return (DPAA2_CMD_STAT_ERR); + + error = dpaa2_rc_exec_cmd(portal, cmd, CMDID_BP_GET_ATTR); + if (!error) { + pattr = (struct dpaa2_bp_attr *) &cmd->params[0]; + attr->id = pattr->id; + attr->bpid = pattr->bpid; + } + + return (error); +} + +static int +dpaa2_rc_mac_open(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint32_t dpmac_id, uint16_t *token) +{ + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + struct dpaa2_cmd_header *hdr; + int error; + + if (portal == NULL || cmd == NULL || token == NULL) + return (DPAA2_CMD_STAT_ERR); + + cmd->params[0] = dpmac_id; + error = dpaa2_rc_exec_cmd(portal, cmd, CMDID_MAC_OPEN); + if (!error) { + hdr = (struct dpaa2_cmd_header *) &cmd->header; + *token = hdr->token; + } + + return (error); +} + +static int +dpaa2_rc_mac_close(device_t dev, device_t child, struct dpaa2_cmd *cmd) +{ + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + + if (portal == NULL || cmd == NULL) + return (DPAA2_CMD_STAT_ERR); + + return (dpaa2_rc_exec_cmd(portal, cmd, CMDID_MAC_CLOSE)); +} + +static int +dpaa2_rc_mac_reset(device_t dev, device_t child, struct dpaa2_cmd *cmd) +{ + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + + if (portal == NULL || cmd == NULL) + return (DPAA2_CMD_STAT_ERR); + + return (dpaa2_rc_exec_cmd(portal, cmd, CMDID_MAC_RESET)); +} + +static int +dpaa2_rc_mac_mdio_read(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint8_t phy, uint16_t reg, uint16_t *val) +{ + struct __packed mdio_read_args { + uint8_t clause; /* set to 0 by default */ + uint8_t phy; + uint16_t reg; + uint32_t _reserved1; + uint64_t _reserved2[6]; + } *args; + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + int error; + + if (portal == NULL || cmd == NULL || val == NULL) + return (DPAA2_CMD_STAT_ERR); + + args = (struct mdio_read_args *) &cmd->params[0]; + args->phy = phy; + args->reg = reg; + args->clause = 0; + + error = dpaa2_rc_exec_cmd(portal, cmd, CMDID_MAC_MDIO_READ); + if (!error) + *val = cmd->params[0] & 0xFFFF; + + return (error); +} + +static int +dpaa2_rc_mac_mdio_write(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint8_t phy, uint16_t reg, uint16_t val) +{ + struct __packed mdio_write_args { + uint8_t clause; /* set to 0 by default */ + uint8_t phy; + uint16_t reg; + uint16_t val; + uint16_t _reserved1; + uint64_t _reserved2[6]; + } *args; + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + + if (portal == NULL || cmd == NULL) + return (DPAA2_CMD_STAT_ERR); + + args = (struct mdio_write_args *) &cmd->params[0]; + args->phy = phy; + args->reg = reg; + args->val = val; + args->clause = 0; + + return (dpaa2_rc_exec_cmd(portal, cmd, CMDID_MAC_MDIO_WRITE)); +} + +static int +dpaa2_rc_mac_get_addr(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint8_t *mac) +{ + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + int error; + + if (portal == NULL || cmd == NULL || mac == NULL) + return (DPAA2_CMD_STAT_ERR); + + error = dpaa2_rc_exec_cmd(portal, cmd, CMDID_MAC_GET_ADDR); + if (!error) { + mac[0] = (cmd->params[0] >> 56) & 0xFFU; + mac[1] = (cmd->params[0] >> 48) & 0xFFU; + mac[2] = (cmd->params[0] >> 40) & 0xFFU; + mac[3] = (cmd->params[0] >> 32) & 0xFFU; + mac[4] = (cmd->params[0] >> 24) & 0xFFU; + mac[5] = (cmd->params[0] >> 16) & 0xFFU; + } + + return (error); +} + +static int +dpaa2_rc_mac_get_attributes(device_t dev, device_t child, struct dpaa2_cmd *cmd, + struct dpaa2_mac_attr *attr) +{ + struct __packed mac_attr_resp { + uint8_t eth_if; + uint8_t link_type; + uint16_t id; + uint32_t max_rate; + + uint8_t fec_mode; + uint8_t ifg_mode; + uint8_t ifg_len; + uint8_t _reserved1; + uint32_t _reserved2; + + uint8_t sgn_post_pre; + uint8_t serdes_cfg_mode; + uint8_t eq_amp_red; + uint8_t eq_post1q; + uint8_t eq_preq; + uint8_t eq_type; + uint16_t _reserved3; + + uint64_t _reserved[4]; + } *resp; + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + int error; + + if (portal == NULL || cmd == NULL || attr == NULL) + return (DPAA2_CMD_STAT_EINVAL); + + error = dpaa2_rc_exec_cmd(portal, cmd, CMDID_MAC_GET_ATTR); + if (!error) { + resp = (struct mac_attr_resp *) &cmd->params[0]; + attr->id = resp->id; + attr->max_rate = resp->max_rate; + attr->eth_if = resp->eth_if; + attr->link_type = resp->link_type; + } + + return (error); +} + +static int +dpaa2_rc_mac_set_link_state(device_t dev, device_t child, struct dpaa2_cmd *cmd, + struct dpaa2_mac_link_state *state) +{ + struct __packed mac_set_link_args { + uint64_t options; + uint32_t rate; + uint32_t _reserved1; + uint32_t flags; + uint32_t _reserved2; + uint64_t supported; + uint64_t advert; + } *args; + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + + if (portal == NULL || cmd == NULL || state == NULL) + return (DPAA2_CMD_STAT_EINVAL); + + dpaa2_rc_reset_cmd_params(cmd); + + args = (struct mac_set_link_args *) &cmd->params[0]; + args->options = state->options; + args->rate = state->rate; + args->supported = state->supported; + args->advert = state->advert; + + args->flags |= state->up ? 0x1u : 0u; + args->flags |= state->state_valid ? 0x2u : 0u; + + return (dpaa2_rc_exec_cmd(portal, cmd, CMDID_MAC_SET_LINK_STATE)); +} + +static int +dpaa2_rc_mac_set_irq_mask(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint8_t irq_idx, uint32_t mask) +{ + /* TODO: Implementation is the same as for ni_set_irq_mask(). */ + struct __packed set_irq_mask_args { + uint32_t mask; + uint8_t irq_idx; + } *args; + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + + if (portal == NULL || cmd == NULL) + return (DPAA2_CMD_STAT_EINVAL); + + dpaa2_rc_reset_cmd_params(cmd); + + args = (struct set_irq_mask_args *) &cmd->params[0]; + args->mask = mask; + args->irq_idx = irq_idx; + + return (dpaa2_rc_exec_cmd(portal, cmd, CMDID_MAC_SET_IRQ_MASK)); +} + +static int +dpaa2_rc_mac_set_irq_enable(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint8_t irq_idx, bool en) +{ + /* TODO: Implementation is the same as for ni_set_irq_enable(). */ + struct __packed set_irq_enable_args { + uint32_t en; + uint8_t irq_idx; + } *args; + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + + if (portal == NULL || cmd == NULL) + return (DPAA2_CMD_STAT_EINVAL); + + dpaa2_rc_reset_cmd_params(cmd); + + args = (struct set_irq_enable_args *) &cmd->params[0]; + args->en = en ? 1u : 0u; + args->irq_idx = irq_idx; + + return (dpaa2_rc_exec_cmd(portal, cmd, CMDID_MAC_SET_IRQ_ENABLE)); +} + +static int +dpaa2_rc_mac_get_irq_status(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint8_t irq_idx, uint32_t *status) +{ + /* TODO: Implementation is the same as ni_get_irq_status(). */ + struct __packed get_irq_stat_args { + uint32_t status; + uint8_t irq_idx; + } *args; + struct __packed get_irq_stat_resp { + uint32_t status; + } *resp; + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + int error; + + if (portal == NULL || cmd == NULL || status == NULL) + return (DPAA2_CMD_STAT_EINVAL); + + dpaa2_rc_reset_cmd_params(cmd); + + args = (struct get_irq_stat_args *) &cmd->params[0]; + args->status = *status; + args->irq_idx = irq_idx; + + error = dpaa2_rc_exec_cmd(portal, cmd, CMDID_MAC_GET_IRQ_STATUS); + if (!error) { + resp = (struct get_irq_stat_resp *) &cmd->params[0]; + *status = resp->status; + } + + return (error); +} + +static int +dpaa2_rc_con_open(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint32_t dpcon_id, uint16_t *token) +{ + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + struct dpaa2_cmd_header *hdr; + int error; + + if (portal == NULL || cmd == NULL || token == NULL) + return (DPAA2_CMD_STAT_ERR); + + cmd->params[0] = dpcon_id; + error = dpaa2_rc_exec_cmd(portal, cmd, CMDID_CON_OPEN); + if (!error) { + hdr = (struct dpaa2_cmd_header *) &cmd->header; + *token = hdr->token; + } + + return (error); +} + + +static int +dpaa2_rc_con_close(device_t dev, device_t child, struct dpaa2_cmd *cmd) +{ + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + + if (portal == NULL || cmd == NULL) + return (DPAA2_CMD_STAT_ERR); + + return (dpaa2_rc_exec_cmd(portal, cmd, CMDID_CON_CLOSE)); +} + +static int +dpaa2_rc_con_reset(device_t dev, device_t child, struct dpaa2_cmd *cmd) +{ + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + + if (portal == NULL || cmd == NULL) + return (DPAA2_CMD_STAT_ERR); + + return (dpaa2_rc_exec_cmd(portal, cmd, CMDID_CON_RESET)); +} + +static int +dpaa2_rc_con_enable(device_t dev, device_t child, struct dpaa2_cmd *cmd) +{ + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + + if (portal == NULL || cmd == NULL) + return (DPAA2_CMD_STAT_ERR); + + return (dpaa2_rc_exec_cmd(portal, cmd, CMDID_CON_ENABLE)); +} + +static int +dpaa2_rc_con_disable(device_t dev, device_t child, struct dpaa2_cmd *cmd) +{ + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + + if (portal == NULL || cmd == NULL) + return (DPAA2_CMD_STAT_ERR); + + return (dpaa2_rc_exec_cmd(portal, cmd, CMDID_CON_DISABLE)); +} + +static int +dpaa2_rc_con_get_attributes(device_t dev, device_t child, struct dpaa2_cmd *cmd, + struct dpaa2_con_attr *attr) +{ + struct __packed con_attr_resp { + uint32_t id; + uint16_t chan_id; + uint8_t prior_num; + uint8_t _reserved1; + uint64_t _reserved2[6]; + } *resp; + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + int error; + + if (portal == NULL || cmd == NULL || attr == NULL) + return (DPAA2_CMD_STAT_EINVAL); + + error = dpaa2_rc_exec_cmd(portal, cmd, CMDID_CON_GET_ATTR); + if (!error) { + resp = (struct con_attr_resp *) &cmd->params[0]; + attr->id = resp->id; + attr->chan_id = resp->chan_id; + attr->prior_num = resp->prior_num; + } + + return (error); +} + +static int +dpaa2_rc_con_set_notif(device_t dev, device_t child, struct dpaa2_cmd *cmd, + struct dpaa2_con_notif_cfg *cfg) +{ + struct __packed set_notif_args { + uint32_t dpio_id; + uint8_t prior; + uint8_t _reserved1; + uint16_t _reserved2; + uint64_t ctx; + uint64_t _reserved3[5]; + } *args; + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + + if (portal == NULL || cmd == NULL || cfg == NULL) + return (DPAA2_CMD_STAT_ERR); + + args = (struct set_notif_args *) &cmd->params[0]; + args->dpio_id = cfg->dpio_id; + args->prior = cfg->prior; + args->ctx = cfg->qman_ctx; + + return (dpaa2_rc_exec_cmd(portal, cmd, CMDID_CON_SET_NOTIF)); +} + +static int +dpaa2_rc_mcp_create(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint32_t portal_id, uint32_t options, uint32_t *dpmcp_id) +{ + struct __packed mcp_create_args { + uint32_t portal_id; + uint32_t options; + uint64_t _reserved[6]; + } *args; + struct __packed mcp_create_resp { + uint32_t dpmcp_id; + } *resp; + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + int error; + + if (portal == NULL || cmd == NULL || dpmcp_id == NULL) + return (DPAA2_CMD_STAT_ERR); + + args = (struct mcp_create_args *) &cmd->params[0]; + args->portal_id = portal_id; + args->options = options; + + error = dpaa2_rc_exec_cmd(portal, cmd, CMDID_MCP_CREATE); + if (!error) { + resp = (struct mcp_create_resp *) &cmd->params[0]; + *dpmcp_id = resp->dpmcp_id; + } + + return (error); +} + +static int +dpaa2_rc_mcp_destroy(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint32_t dpmcp_id) +{ + struct __packed mcp_destroy_args { + uint32_t dpmcp_id; + } *args; + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + + if (portal == NULL || cmd == NULL) + return (DPAA2_CMD_STAT_ERR); + + args = (struct mcp_destroy_args *) &cmd->params[0]; + args->dpmcp_id = dpmcp_id; + + return (dpaa2_rc_exec_cmd(portal, cmd, CMDID_MCP_DESTROY)); +} + +static int +dpaa2_rc_mcp_open(device_t dev, device_t child, struct dpaa2_cmd *cmd, + uint32_t dpmcp_id, uint16_t *token) +{ + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + struct dpaa2_cmd_header *hdr; + int error; + + if (portal == NULL || cmd == NULL || token == NULL) + return (DPAA2_CMD_STAT_ERR); + + cmd->params[0] = dpmcp_id; + error = dpaa2_rc_exec_cmd(portal, cmd, CMDID_MCP_OPEN); + if (!error) { + hdr = (struct dpaa2_cmd_header *) &cmd->header; + *token = hdr->token; + } + + return (error); +} + +static int +dpaa2_rc_mcp_close(device_t dev, device_t child, struct dpaa2_cmd *cmd) +{ + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + + if (portal == NULL || cmd == NULL) + return (DPAA2_CMD_STAT_ERR); + + return (dpaa2_rc_exec_cmd(portal, cmd, CMDID_MCP_CLOSE)); +} + +static int +dpaa2_rc_mcp_reset(device_t dev, device_t child, struct dpaa2_cmd *cmd) +{ + struct dpaa2_mcp *portal = dpaa2_rc_select_portal(dev, child); + + if (portal == NULL || cmd == NULL) + return (DPAA2_CMD_STAT_ERR); + + return (dpaa2_rc_exec_cmd(portal, cmd, CMDID_MCP_RESET)); +} + +/** + * @brief Create and add devices for DPAA2 objects in this resource container. + */ +static int +dpaa2_rc_discover(struct dpaa2_rc_softc *sc) +{ + device_t rcdev = sc->dev; + device_t child = sc->dev; + struct dpaa2_devinfo *rcinfo = device_get_ivars(rcdev); + struct dpaa2_cmd *cmd = NULL; + struct dpaa2_rc_attr dprc_attr; + struct dpaa2_obj obj; + uint32_t major, minor, rev, obj_count; + uint16_t rc_token; + int rc; + + /* Allocate a command to send to MC hardware. */ + rc = dpaa2_mcp_init_command(&cmd, DPAA2_CMD_DEF); + if (rc) { + device_printf(rcdev, "%s: failed to allocate dpaa2_cmd: " + "error=%d\n", __func__, rc); + return (ENXIO); + } + + /* Print MC firmware version. */ + rc = DPAA2_CMD_MNG_GET_VERSION(rcdev, child, cmd, &major, &minor, &rev); + if (rc) { + device_printf(rcdev, "%s: failed to get MC firmware version: " + "error=%d\n", __func__, rc); + dpaa2_mcp_free_command(cmd); + return (ENXIO); + } + device_printf(rcdev, "MC firmware version: %u.%u.%u\n", major, minor, + rev); + + /* Obtain container ID associated with a given MC portal. */ + rc = DPAA2_CMD_MNG_GET_CONTAINER_ID(rcdev, child, cmd, &sc->cont_id); + if (rc) { + device_printf(rcdev, "%s: failed to get container id: " + "error=%d\n", __func__, rc); + dpaa2_mcp_free_command(cmd); + return (ENXIO); + } + if (bootverbose) + device_printf(rcdev, "Resource container ID: %u\n", sc->cont_id); + + /* Open the resource container. */ + rc = DPAA2_CMD_RC_OPEN(rcdev, child, cmd, sc->cont_id, &rc_token); + if (rc) { + device_printf(rcdev, "%s: failed to open container: cont_id=%u, " + "error=%d\n", __func__, sc->cont_id, rc); + dpaa2_mcp_free_command(cmd); + return (ENXIO); + } + + /* Obtain a number of objects in this container. */ + rc = DPAA2_CMD_RC_GET_OBJ_COUNT(rcdev, child, cmd, &obj_count); + if (rc) { + device_printf(rcdev, "%s: failed to count objects in container: " + "cont_id=%u, error=%d\n", __func__, sc->cont_id, rc); + DPAA2_CMD_RC_CLOSE(rcdev, child, cmd); + dpaa2_mcp_free_command(cmd); + return (ENXIO); + } + if (bootverbose) + device_printf(rcdev, "Objects in container: %u\n", obj_count); + + /* Obtain container attributes (including ICID). */ + rc = DPAA2_CMD_RC_GET_ATTRIBUTES(rcdev, child, cmd, &dprc_attr); + if (rc) { + device_printf(rcdev, "%s: failed to get attributes of the " + "container: cont_id=%u, error=%d\n", __func__, sc->cont_id, + rc); + DPAA2_CMD_RC_CLOSE(rcdev, child, cmd); + dpaa2_mcp_free_command(cmd); + return (ENXIO); + } + if (bootverbose) + device_printf(rcdev, "Isolation context ID: %u\n", + dprc_attr.icid); + if (rcinfo) { + rcinfo->id = dprc_attr.cont_id; + rcinfo->portal_id = dprc_attr.portal_id; + rcinfo->icid = dprc_attr.icid; + } + + /* + * Add MC portals before everything else. + * TODO: Discover DPAA2 objects on-demand. + */ + for (uint32_t i = 0; i < obj_count; i++) { + rc = DPAA2_CMD_RC_GET_OBJ(rcdev, child, cmd, i, &obj); + if (rc) + continue; /* Skip silently for now. */ + if (obj.type != DPAA2_DEV_MCP) + continue; + + dpaa2_rc_add_managed_child(sc, cmd, &obj); + } + /* Probe and attach MC portals. */ + bus_generic_probe(rcdev); + rc = bus_generic_attach(rcdev); + if (rc) { + DPAA2_CMD_RC_CLOSE(rcdev, child, cmd); + dpaa2_mcp_free_command(cmd); + return (rc); + } + + /* Add managed devices (except DPMCPs) to the resource container. */ + for (uint32_t i = 0; i < obj_count; i++) { + rc = DPAA2_CMD_RC_GET_OBJ(rcdev, child, cmd, i, &obj); + if (rc && bootverbose) { + if (rc == DPAA2_CMD_STAT_UNKNOWN_OBJ) { + device_printf(rcdev, "%s: skip unsupported " + "DPAA2 object: idx=%u\n", __func__, i); + continue; + } else { + device_printf(rcdev, "%s: failed to get " + "information about DPAA2 object: idx=%u, " + "error=%d\n", __func__, i, rc); + continue; + } + } + if (obj.type == DPAA2_DEV_MCP) + continue; /* Already added. */ + + dpaa2_rc_add_managed_child(sc, cmd, &obj); + } + /* Probe and attach managed devices properly. */ + bus_generic_probe(rcdev); + rc = bus_generic_attach(rcdev); + if (rc) { + DPAA2_CMD_RC_CLOSE(rcdev, child, cmd); + dpaa2_mcp_free_command(cmd); + return (rc); + } + + /* Add other devices to the resource container. */ + for (uint32_t i = 0; i < obj_count; i++) { + rc = DPAA2_CMD_RC_GET_OBJ(rcdev, child, cmd, i, &obj); + if (rc == DPAA2_CMD_STAT_UNKNOWN_OBJ && bootverbose) { + device_printf(rcdev, "%s: skip unsupported DPAA2 " + "object: idx=%u\n", __func__, i); + continue; + } else if (rc) { + device_printf(rcdev, "%s: failed to get object: " + "idx=%u, error=%d\n", __func__, i, rc); + continue; + } + dpaa2_rc_add_child(sc, cmd, &obj); + } + + DPAA2_CMD_RC_CLOSE(rcdev, child, cmd); + dpaa2_mcp_free_command(cmd); + + /* Probe and attach the rest of devices. */ + bus_generic_probe(rcdev); + return (bus_generic_attach(rcdev)); +} + +/** + * @brief Add a new DPAA2 device to the resource container bus. + */ +static int +dpaa2_rc_add_child(struct dpaa2_rc_softc *sc, struct dpaa2_cmd *cmd, + struct dpaa2_obj *obj) +{ + device_t rcdev, dev; + struct dpaa2_devinfo *rcinfo; + struct dpaa2_devinfo *dinfo; + struct resource_spec *res_spec; + const char *devclass; + int dpio_n = 0; /* to limit DPIOs by # of CPUs */ + int dpcon_n = 0; /* to limit DPCONs by # of CPUs */ + int rid, error; + + rcdev = sc->dev; + rcinfo = device_get_ivars(rcdev); + + switch (obj->type) { + case DPAA2_DEV_NI: + devclass = "dpaa2_ni"; + res_spec = dpaa2_ni_spec; + break; + default: + return (ENXIO); + } + + /* Add a device for the DPAA2 object. */ + dev = device_add_child(rcdev, devclass, -1); + if (dev == NULL) { + device_printf(rcdev, "%s: failed to add a device for DPAA2 " + "object: type=%s, id=%u\n", __func__, dpaa2_ttos(obj->type), + obj->id); + return (ENXIO); + } + + /* Allocate devinfo for a child. */ + dinfo = malloc(sizeof(struct dpaa2_devinfo), M_DPAA2_RC, + M_WAITOK | M_ZERO); + if (!dinfo) { + device_printf(rcdev, "%s: failed to allocate dpaa2_devinfo " + "for: type=%s, id=%u\n", __func__, dpaa2_ttos(obj->type), + obj->id); + return (ENXIO); + } + device_set_ivars(dev, dinfo); + + dinfo->pdev = rcdev; + dinfo->dev = dev; + dinfo->id = obj->id; + dinfo->dtype = obj->type; + dinfo->portal = NULL; + /* Children share their parent container's ICID and portal ID. */ + dinfo->icid = rcinfo->icid; + dinfo->portal_id = rcinfo->portal_id; + /* MSI configuration */ + dinfo->msi.msi_msgnum = obj->irq_count; + dinfo->msi.msi_alloc = 0; + dinfo->msi.msi_handlers = 0; + + /* Initialize a resource list for the child. */ + resource_list_init(&dinfo->resources); + + /* Add DPAA2-specific resources to the resource list. */ + for (; res_spec && res_spec->type != -1; res_spec++) { + if (res_spec->type < DPAA2_DEV_MC) + continue; /* Skip non-DPAA2 resource. */ + rid = res_spec->rid; + + /* Limit DPIOs and DPCONs by number of CPUs. */ + if (res_spec->type == DPAA2_DEV_IO && dpio_n >= mp_ncpus) { + dpio_n++; + continue; + } + if (res_spec->type == DPAA2_DEV_CON && dpcon_n >= mp_ncpus) { + dpcon_n++; + continue; + } + + error = dpaa2_rc_add_res(rcdev, dev, res_spec->type, &rid, + res_spec->flags); + if (error) + device_printf(rcdev, "%s: dpaa2_rc_add_res() failed: " + "error=%d\n", __func__, error); + + if (res_spec->type == DPAA2_DEV_IO) + dpio_n++; + if (res_spec->type == DPAA2_DEV_CON) + dpcon_n++; + } + + return (0); +} + +/** + * @brief Add a new managed DPAA2 device to the resource container bus. + * + * There are DPAA2 objects (DPIO, DPBP) which have their own drivers and can be + * allocated as resources or associated with the other DPAA2 objects. This + * function is supposed to discover such managed objects in the resource + * container and add them as children to perform a proper initialization. + * + * NOTE: It must be called together with bus_generic_probe() and + * bus_generic_attach() before dpaa2_rc_add_child(). + */ +static int +dpaa2_rc_add_managed_child(struct dpaa2_rc_softc *sc, struct dpaa2_cmd *cmd, + struct dpaa2_obj *obj) +{ + device_t rcdev, dev, child; + struct dpaa2_devinfo *rcinfo, *dinfo; + struct dpaa2_rc_obj_region reg; + struct resource_spec *res_spec; + const char *devclass; + uint64_t start, end, count; + uint32_t flags = 0; + int rid, error; + + rcdev = sc->dev; + child = sc->dev; + rcinfo = device_get_ivars(rcdev); + + switch (obj->type) { + case DPAA2_DEV_IO: + devclass = "dpaa2_io"; + res_spec = dpaa2_io_spec; + flags = DPAA2_MC_DEV_ALLOCATABLE | DPAA2_MC_DEV_SHAREABLE; + break; + case DPAA2_DEV_BP: + devclass = "dpaa2_bp"; + res_spec = dpaa2_bp_spec; + flags = DPAA2_MC_DEV_ALLOCATABLE; + break; + case DPAA2_DEV_CON: + devclass = "dpaa2_con"; + res_spec = dpaa2_con_spec; + flags = DPAA2_MC_DEV_ALLOCATABLE; + break; + case DPAA2_DEV_MAC: + devclass = "dpaa2_mac"; + res_spec = dpaa2_mac_spec; + flags = DPAA2_MC_DEV_ASSOCIATED; + break; + case DPAA2_DEV_MCP: + devclass = "dpaa2_mcp"; + res_spec = NULL; + flags = DPAA2_MC_DEV_ALLOCATABLE | DPAA2_MC_DEV_SHAREABLE; + break; + default: + /* Only managed devices above are supported. */ + return (EINVAL); + } + + /* Add a device for the DPAA2 object. */ + dev = device_add_child(rcdev, devclass, -1); + if (dev == NULL) { + device_printf(rcdev, "%s: failed to add a device for DPAA2 " + "object: type=%s, id=%u\n", __func__, dpaa2_ttos(obj->type), + obj->id); + return (ENXIO); + } + + /* Allocate devinfo for the child. */ + dinfo = malloc(sizeof(struct dpaa2_devinfo), M_DPAA2_RC, + M_WAITOK | M_ZERO); + if (!dinfo) { + device_printf(rcdev, "%s: failed to allocate dpaa2_devinfo " + "for: type=%s, id=%u\n", __func__, dpaa2_ttos(obj->type), + obj->id); + return (ENXIO); + } + device_set_ivars(dev, dinfo); + + dinfo->pdev = rcdev; + dinfo->dev = dev; + dinfo->id = obj->id; + dinfo->dtype = obj->type; + dinfo->portal = NULL; + /* Children share their parent container's ICID and portal ID. */ + dinfo->icid = rcinfo->icid; + dinfo->portal_id = rcinfo->portal_id; + /* MSI configuration */ + dinfo->msi.msi_msgnum = obj->irq_count; + dinfo->msi.msi_alloc = 0; + dinfo->msi.msi_handlers = 0; + + /* Initialize a resource list for the child. */ + resource_list_init(&dinfo->resources); + + /* Add memory regions to the resource list. */ + for (uint8_t i = 0; i < obj->reg_count; i++) { + error = DPAA2_CMD_RC_GET_OBJ_REGION(rcdev, child, cmd, obj->id, + i, obj->type, ®); + if (error) { + device_printf(rcdev, "%s: failed to obtain memory " + "region for type=%s, id=%u, reg_idx=%u: error=%d\n", + __func__, dpaa2_ttos(obj->type), obj->id, i, error); + continue; + } + count = reg.size; + start = reg.base_paddr + reg.base_offset; + end = reg.base_paddr + reg.base_offset + reg.size - 1; + + resource_list_add(&dinfo->resources, SYS_RES_MEMORY, i, start, + end, count); + } + + /* Add DPAA2-specific resources to the resource list. */ + for (; res_spec && res_spec->type != -1; res_spec++) { + if (res_spec->type < DPAA2_DEV_MC) + continue; /* Skip non-DPAA2 resource. */ + rid = res_spec->rid; + + error = dpaa2_rc_add_res(rcdev, dev, res_spec->type, &rid, + res_spec->flags); + if (error) + device_printf(rcdev, "%s: dpaa2_rc_add_res() failed: " + "error=%d\n", __func__, error); + } + + /* Inform MC about a new managed device. */ + error = DPAA2_MC_MANAGE_DEV(rcdev, dev, flags); + if (error) { + device_printf(rcdev, "%s: failed to add a managed DPAA2 device: " + "type=%s, id=%u, error=%d\n", __func__, + dpaa2_ttos(obj->type), obj->id, error); + return (ENXIO); + } + + return (0); +} + +/** + * @brief Configure given IRQ using MC command interface. + */ +static int +dpaa2_rc_configure_irq(device_t rcdev, device_t child, int rid, uint64_t addr, + uint32_t data) +{ + struct dpaa2_devinfo *rcinfo; + struct dpaa2_devinfo *dinfo; + struct dpaa2_cmd *cmd; + uint16_t rc_token; + int rc = EINVAL; + + if (device_get_parent(child) == rcdev && rid >= 1) { + rcinfo = device_get_ivars(rcdev); + dinfo = device_get_ivars(child); + + /* Allocate a command to send to MC hardware. */ + rc = dpaa2_mcp_init_command(&cmd, DPAA2_CMD_DEF); + if (rc) { + device_printf(rcdev, "%s: failed to allocate dpaa2_cmd: " + "error=%d\n", __func__, rc); + return (ENODEV); + } + + /* Open resource container. */ + rc = DPAA2_CMD_RC_OPEN(rcdev, child, cmd, rcinfo->id, &rc_token); + if (rc) { + dpaa2_mcp_free_command(cmd); + device_printf(rcdev, "%s: failed to open DPRC: " + "error=%d\n", __func__, rc); + return (ENODEV); + } + /* Set MSI address and value. */ + rc = DPAA2_CMD_RC_SET_OBJ_IRQ(rcdev, child, cmd, rid - 1, addr, + data, rid, dinfo->id, dinfo->dtype); + if (rc) { + dpaa2_mcp_free_command(cmd); + device_printf(rcdev, "%s: failed to setup IRQ: " + "rid=%d, addr=%jx, data=%x, error=%d\n", __func__, + rid, addr, data, rc); + return (ENODEV); + } + /* Close resource container. */ + rc = DPAA2_CMD_RC_CLOSE(rcdev, child, cmd); + if (rc) { + dpaa2_mcp_free_command(cmd); + device_printf(rcdev, "%s: failed to close DPRC: " + "error=%d\n", __func__, rc); + return (ENODEV); + } + + dpaa2_mcp_free_command(cmd); + rc = 0; + } + + return (rc); +} + +/** + * @brief General implementation of the MC command to enable IRQ. + */ +static int +dpaa2_rc_enable_irq(struct dpaa2_mcp *mcp, struct dpaa2_cmd *cmd, + uint8_t irq_idx, bool enable, uint16_t cmdid) +{ + struct __packed enable_irq_args { + uint8_t enable; + uint8_t _reserved1; + uint16_t _reserved2; + uint8_t irq_idx; + uint8_t _reserved3; + uint16_t _reserved4; + uint64_t _reserved5[6]; + } *args; + + if (!mcp || !cmd) + return (DPAA2_CMD_STAT_ERR); + + args = (struct enable_irq_args *) &cmd->params[0]; + args->irq_idx = irq_idx; + args->enable = enable == 0u ? 0u : 1u; + + return (dpaa2_rc_exec_cmd(mcp, cmd, cmdid)); +} + +/** + * @brief Sends a command to MC and waits for response. + */ +static int +dpaa2_rc_exec_cmd(struct dpaa2_mcp *mcp, struct dpaa2_cmd *cmd, uint16_t cmdid) +{ + struct dpaa2_cmd_header *hdr; + uint16_t flags; + int error; + + if (!mcp || !cmd) + return (DPAA2_CMD_STAT_ERR); + + /* Prepare a command for the MC hardware. */ + hdr = (struct dpaa2_cmd_header *) &cmd->header; + hdr->cmdid = cmdid; + hdr->status = DPAA2_CMD_STAT_READY; + + DPAA2_MCP_LOCK(mcp, &flags); + if (flags & DPAA2_PORTAL_DESTROYED) { + /* Terminate operation if portal is destroyed. */ + DPAA2_MCP_UNLOCK(mcp); + return (DPAA2_CMD_STAT_INVALID_STATE); + } + + /* Send a command to MC and wait for the result. */ + dpaa2_rc_send_cmd(mcp, cmd); + error = dpaa2_rc_wait_for_cmd(mcp, cmd); + if (error) { + DPAA2_MCP_UNLOCK(mcp); + return (DPAA2_CMD_STAT_ERR); + } + if (hdr->status != DPAA2_CMD_STAT_OK) { + DPAA2_MCP_UNLOCK(mcp); + return (int)(hdr->status); + } + + DPAA2_MCP_UNLOCK(mcp); + + return (DPAA2_CMD_STAT_OK); +} + +/** + * @brief Writes a command to the MC command portal. + */ +static int +dpaa2_rc_send_cmd(struct dpaa2_mcp *mcp, struct dpaa2_cmd *cmd) +{ + /* Write command parameters. */ + for (uint32_t i = 1; i <= DPAA2_CMD_PARAMS_N; i++) + bus_write_8(mcp->map, sizeof(uint64_t) * i, cmd->params[i-1]); + + bus_barrier(mcp->map, 0, sizeof(struct dpaa2_cmd), + BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); + + /* Write command header to trigger execution. */ + bus_write_8(mcp->map, 0, cmd->header); + + return (0); +} + +/** + * @brief Polls the MC command portal in order to receive a result of the + * command execution. + */ +static int +dpaa2_rc_wait_for_cmd(struct dpaa2_mcp *mcp, struct dpaa2_cmd *cmd) +{ + struct dpaa2_cmd_header *hdr; + uint64_t val; + uint32_t i; + + /* Wait for a command execution result from the MC hardware. */ + for (i = 1; i <= CMD_SPIN_ATTEMPTS; i++) { + val = bus_read_8(mcp->map, 0); + hdr = (struct dpaa2_cmd_header *) &val; + if (hdr->status != DPAA2_CMD_STAT_READY) { + break; + } + DELAY(CMD_SPIN_TIMEOUT); + } + + if (i > CMD_SPIN_ATTEMPTS) { + /* Return an error on expired timeout. */ + return (DPAA2_CMD_STAT_TIMEOUT); + } else { + /* Read command response. */ + cmd->header = val; + for (i = 1; i <= DPAA2_CMD_PARAMS_N; i++) { + cmd->params[i-1] = + bus_read_8(mcp->map, i * sizeof(uint64_t)); + } + } + + return (DPAA2_CMD_STAT_OK); +} + +/** + * @brief Reserve a DPAA2-specific device of the given devtype for the child. + */ +static int +dpaa2_rc_add_res(device_t rcdev, device_t child, enum dpaa2_dev_type devtype, + int *rid, int flags) +{ + device_t dpaa2_dev; + struct dpaa2_devinfo *dinfo = device_get_ivars(child); + struct resource *res; + bool shared = false; + int error; + + /* Request a free DPAA2 device of the given type from MC. */ + error = DPAA2_MC_GET_FREE_DEV(rcdev, &dpaa2_dev, devtype); + if (error && !(flags & RF_SHAREABLE)) { + device_printf(rcdev, "%s: failed to obtain a free %s (rid=%d) " + "for: %s (id=%u)\n", __func__, dpaa2_ttos(devtype), *rid, + dpaa2_ttos(dinfo->dtype), dinfo->id); + return (error); + } + + /* Request a shared DPAA2 device of the given type from MC. */ + if (error) { + error = DPAA2_MC_GET_SHARED_DEV(rcdev, &dpaa2_dev, devtype); + if (error) { + device_printf(rcdev, "%s: failed to obtain a shared " + "%s (rid=%d) for: %s (id=%u)\n", __func__, + dpaa2_ttos(devtype), *rid, dpaa2_ttos(dinfo->dtype), + dinfo->id); + return (error); + } + shared = true; + } + + /* Add DPAA2 device to the resource list of the child device. */ + resource_list_add(&dinfo->resources, devtype, *rid, + (rman_res_t) dpaa2_dev, (rman_res_t) dpaa2_dev, 1); + + /* Reserve a newly added DPAA2 resource. */ + res = resource_list_reserve(&dinfo->resources, rcdev, child, devtype, + rid, (rman_res_t) dpaa2_dev, (rman_res_t) dpaa2_dev, 1, + flags & ~RF_ACTIVE); + if (!res) { + device_printf(rcdev, "%s: failed to reserve %s (rid=%d) for: %s " + "(id=%u)\n", __func__, dpaa2_ttos(devtype), *rid, + dpaa2_ttos(dinfo->dtype), dinfo->id); + return (EBUSY); + } + + /* Reserve a shared DPAA2 device of the given type. */ + if (shared) { + error = DPAA2_MC_RESERVE_DEV(rcdev, dpaa2_dev, devtype); + if (error) { + device_printf(rcdev, "%s: failed to reserve a shared " + "%s (rid=%d) for: %s (id=%u)\n", __func__, + dpaa2_ttos(devtype), *rid, dpaa2_ttos(dinfo->dtype), + dinfo->id); + return (error); + } + } + + return (0); +} + +static int +dpaa2_rc_print_type(struct resource_list *rl, enum dpaa2_dev_type type) +{ + struct dpaa2_devinfo *dinfo; + struct resource_list_entry *rle; + uint32_t prev_id; + int printed = 0, series = 0; + int retval = 0; + + STAILQ_FOREACH(rle, rl, link) { + if (rle->type == type) { + dinfo = device_get_ivars((device_t) rle->start); + + if (printed == 0) { + retval += printf(" %s (id=", + dpaa2_ttos(dinfo->dtype)); + } else { + if (dinfo->id == prev_id + 1) { + if (series == 0) { + series = 1; + retval += printf("-"); + } + } else { + if (series == 1) { + retval += printf("%u", prev_id); + series = 0; + } + retval += printf(","); + } + } + printed++; + + if (series == 0) + retval += printf("%u", dinfo->id); + prev_id = dinfo->id; + } + } + if (printed) { + if (series == 1) + retval += printf("%u", prev_id); + retval += printf(")"); + } + + return (retval); +} + +static int +dpaa2_rc_reset_cmd_params(struct dpaa2_cmd *cmd) +{ + if (cmd != NULL) { + memset(cmd->params, 0, sizeof(cmd->params[0]) * + DPAA2_CMD_PARAMS_N); + } + return (0); +} + +static struct dpaa2_mcp * +dpaa2_rc_select_portal(device_t dev, device_t child) +{ + struct dpaa2_devinfo *dinfo = device_get_ivars(dev); + struct dpaa2_devinfo *cinfo = device_get_ivars(child); + + if (cinfo == NULL || dinfo == NULL || dinfo->dtype != DPAA2_DEV_RC) + return (NULL); + return (cinfo->portal != NULL ? cinfo->portal : dinfo->portal); +} + +static device_method_t dpaa2_rc_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, dpaa2_rc_probe), + DEVMETHOD(device_attach, dpaa2_rc_attach), + DEVMETHOD(device_detach, dpaa2_rc_detach), + + /* Bus interface */ + DEVMETHOD(bus_get_resource_list, dpaa2_rc_get_resource_list), + DEVMETHOD(bus_delete_resource, dpaa2_rc_delete_resource), + DEVMETHOD(bus_alloc_resource, dpaa2_rc_alloc_resource), + DEVMETHOD(bus_release_resource, dpaa2_rc_release_resource), + DEVMETHOD(bus_child_deleted, dpaa2_rc_child_deleted), + DEVMETHOD(bus_child_detached, dpaa2_rc_child_detached), + DEVMETHOD(bus_setup_intr, dpaa2_rc_setup_intr), + DEVMETHOD(bus_teardown_intr, dpaa2_rc_teardown_intr), + DEVMETHOD(bus_print_child, dpaa2_rc_print_child), + DEVMETHOD(bus_add_child, device_add_child_ordered), + DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource), + DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), + DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), + DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), + DEVMETHOD(bus_adjust_resource, bus_generic_adjust_resource), + + /* Pseudo-PCI interface */ + DEVMETHOD(pci_alloc_msi, dpaa2_rc_alloc_msi), + DEVMETHOD(pci_release_msi, dpaa2_rc_release_msi), + DEVMETHOD(pci_msi_count, dpaa2_rc_msi_count), + DEVMETHOD(pci_get_id, dpaa2_rc_get_id), + + /* DPAA2 MC command interface */ + DEVMETHOD(dpaa2_cmd_mng_get_version, dpaa2_rc_mng_get_version), + DEVMETHOD(dpaa2_cmd_mng_get_soc_version, dpaa2_rc_mng_get_soc_version), + DEVMETHOD(dpaa2_cmd_mng_get_container_id, dpaa2_rc_mng_get_container_id), + /* DPRC commands */ + DEVMETHOD(dpaa2_cmd_rc_open, dpaa2_rc_open), + DEVMETHOD(dpaa2_cmd_rc_close, dpaa2_rc_close), + DEVMETHOD(dpaa2_cmd_rc_get_obj_count, dpaa2_rc_get_obj_count), + DEVMETHOD(dpaa2_cmd_rc_get_obj, dpaa2_rc_get_obj), + DEVMETHOD(dpaa2_cmd_rc_get_obj_descriptor, dpaa2_rc_get_obj_descriptor), + DEVMETHOD(dpaa2_cmd_rc_get_attributes, dpaa2_rc_get_attributes), + DEVMETHOD(dpaa2_cmd_rc_get_obj_region, dpaa2_rc_get_obj_region), + DEVMETHOD(dpaa2_cmd_rc_get_api_version, dpaa2_rc_get_api_version), + DEVMETHOD(dpaa2_cmd_rc_set_irq_enable, dpaa2_rc_set_irq_enable), + DEVMETHOD(dpaa2_cmd_rc_set_obj_irq, dpaa2_rc_set_obj_irq), + DEVMETHOD(dpaa2_cmd_rc_get_conn, dpaa2_rc_get_conn), + /* DPNI commands */ + DEVMETHOD(dpaa2_cmd_ni_open, dpaa2_rc_ni_open), + DEVMETHOD(dpaa2_cmd_ni_close, dpaa2_rc_ni_close), + DEVMETHOD(dpaa2_cmd_ni_enable, dpaa2_rc_ni_enable), + DEVMETHOD(dpaa2_cmd_ni_disable, dpaa2_rc_ni_disable), + DEVMETHOD(dpaa2_cmd_ni_get_api_version, dpaa2_rc_ni_get_api_version), + DEVMETHOD(dpaa2_cmd_ni_reset, dpaa2_rc_ni_reset), + DEVMETHOD(dpaa2_cmd_ni_get_attributes, dpaa2_rc_ni_get_attributes), + DEVMETHOD(dpaa2_cmd_ni_set_buf_layout, dpaa2_rc_ni_set_buf_layout), + DEVMETHOD(dpaa2_cmd_ni_get_tx_data_off, dpaa2_rc_ni_get_tx_data_offset), + DEVMETHOD(dpaa2_cmd_ni_get_port_mac_addr, dpaa2_rc_ni_get_port_mac_addr), + DEVMETHOD(dpaa2_cmd_ni_set_prim_mac_addr, dpaa2_rc_ni_set_prim_mac_addr), + DEVMETHOD(dpaa2_cmd_ni_get_prim_mac_addr, dpaa2_rc_ni_get_prim_mac_addr), + DEVMETHOD(dpaa2_cmd_ni_set_link_cfg, dpaa2_rc_ni_set_link_cfg), + DEVMETHOD(dpaa2_cmd_ni_get_link_cfg, dpaa2_rc_ni_get_link_cfg), + DEVMETHOD(dpaa2_cmd_ni_get_link_state, dpaa2_rc_ni_get_link_state), + DEVMETHOD(dpaa2_cmd_ni_set_qos_table, dpaa2_rc_ni_set_qos_table), + DEVMETHOD(dpaa2_cmd_ni_clear_qos_table, dpaa2_rc_ni_clear_qos_table), + DEVMETHOD(dpaa2_cmd_ni_set_pools, dpaa2_rc_ni_set_pools), + DEVMETHOD(dpaa2_cmd_ni_set_err_behavior,dpaa2_rc_ni_set_err_behavior), + DEVMETHOD(dpaa2_cmd_ni_get_queue, dpaa2_rc_ni_get_queue), + DEVMETHOD(dpaa2_cmd_ni_set_queue, dpaa2_rc_ni_set_queue), + DEVMETHOD(dpaa2_cmd_ni_get_qdid, dpaa2_rc_ni_get_qdid), + DEVMETHOD(dpaa2_cmd_ni_add_mac_addr, dpaa2_rc_ni_add_mac_addr), + DEVMETHOD(dpaa2_cmd_ni_remove_mac_addr, dpaa2_rc_ni_remove_mac_addr), + DEVMETHOD(dpaa2_cmd_ni_clear_mac_filters, dpaa2_rc_ni_clear_mac_filters), + DEVMETHOD(dpaa2_cmd_ni_set_mfl, dpaa2_rc_ni_set_mfl), + DEVMETHOD(dpaa2_cmd_ni_set_offload, dpaa2_rc_ni_set_offload), + DEVMETHOD(dpaa2_cmd_ni_set_irq_mask, dpaa2_rc_ni_set_irq_mask), + DEVMETHOD(dpaa2_cmd_ni_set_irq_enable, dpaa2_rc_ni_set_irq_enable), + DEVMETHOD(dpaa2_cmd_ni_get_irq_status, dpaa2_rc_ni_get_irq_status), + DEVMETHOD(dpaa2_cmd_ni_set_uni_promisc, dpaa2_rc_ni_set_uni_promisc), + DEVMETHOD(dpaa2_cmd_ni_set_multi_promisc, dpaa2_rc_ni_set_multi_promisc), + DEVMETHOD(dpaa2_cmd_ni_get_statistics, dpaa2_rc_ni_get_statistics), + DEVMETHOD(dpaa2_cmd_ni_set_rx_tc_dist, dpaa2_rc_ni_set_rx_tc_dist), + /* DPIO commands */ + DEVMETHOD(dpaa2_cmd_io_open, dpaa2_rc_io_open), + DEVMETHOD(dpaa2_cmd_io_close, dpaa2_rc_io_close), + DEVMETHOD(dpaa2_cmd_io_enable, dpaa2_rc_io_enable), + DEVMETHOD(dpaa2_cmd_io_disable, dpaa2_rc_io_disable), + DEVMETHOD(dpaa2_cmd_io_reset, dpaa2_rc_io_reset), + DEVMETHOD(dpaa2_cmd_io_get_attributes, dpaa2_rc_io_get_attributes), + DEVMETHOD(dpaa2_cmd_io_set_irq_mask, dpaa2_rc_io_set_irq_mask), + DEVMETHOD(dpaa2_cmd_io_get_irq_status, dpaa2_rc_io_get_irq_status), + DEVMETHOD(dpaa2_cmd_io_set_irq_enable, dpaa2_rc_io_set_irq_enable), + DEVMETHOD(dpaa2_cmd_io_add_static_dq_chan, dpaa2_rc_io_add_static_dq_chan), + /* DPBP commands */ + DEVMETHOD(dpaa2_cmd_bp_open, dpaa2_rc_bp_open), + DEVMETHOD(dpaa2_cmd_bp_close, dpaa2_rc_bp_close), + DEVMETHOD(dpaa2_cmd_bp_enable, dpaa2_rc_bp_enable), + DEVMETHOD(dpaa2_cmd_bp_disable, dpaa2_rc_bp_disable), + DEVMETHOD(dpaa2_cmd_bp_reset, dpaa2_rc_bp_reset), + DEVMETHOD(dpaa2_cmd_bp_get_attributes, dpaa2_rc_bp_get_attributes), + /* DPMAC commands */ + DEVMETHOD(dpaa2_cmd_mac_open, dpaa2_rc_mac_open), + DEVMETHOD(dpaa2_cmd_mac_close, dpaa2_rc_mac_close), + DEVMETHOD(dpaa2_cmd_mac_reset, dpaa2_rc_mac_reset), + DEVMETHOD(dpaa2_cmd_mac_mdio_read, dpaa2_rc_mac_mdio_read), + DEVMETHOD(dpaa2_cmd_mac_mdio_write, dpaa2_rc_mac_mdio_write), + DEVMETHOD(dpaa2_cmd_mac_get_addr, dpaa2_rc_mac_get_addr), + DEVMETHOD(dpaa2_cmd_mac_get_attributes, dpaa2_rc_mac_get_attributes), + DEVMETHOD(dpaa2_cmd_mac_set_link_state, dpaa2_rc_mac_set_link_state), + DEVMETHOD(dpaa2_cmd_mac_set_irq_mask, dpaa2_rc_mac_set_irq_mask), + DEVMETHOD(dpaa2_cmd_mac_set_irq_enable, dpaa2_rc_mac_set_irq_enable), + DEVMETHOD(dpaa2_cmd_mac_get_irq_status, dpaa2_rc_mac_get_irq_status), + /* DPCON commands */ + DEVMETHOD(dpaa2_cmd_con_open, dpaa2_rc_con_open), + DEVMETHOD(dpaa2_cmd_con_close, dpaa2_rc_con_close), + DEVMETHOD(dpaa2_cmd_con_reset, dpaa2_rc_con_reset), + DEVMETHOD(dpaa2_cmd_con_enable, dpaa2_rc_con_enable), + DEVMETHOD(dpaa2_cmd_con_disable, dpaa2_rc_con_disable), + DEVMETHOD(dpaa2_cmd_con_get_attributes, dpaa2_rc_con_get_attributes), + DEVMETHOD(dpaa2_cmd_con_set_notif, dpaa2_rc_con_set_notif), + /* DPMCP commands */ + DEVMETHOD(dpaa2_cmd_mcp_create, dpaa2_rc_mcp_create), + DEVMETHOD(dpaa2_cmd_mcp_destroy, dpaa2_rc_mcp_destroy), + DEVMETHOD(dpaa2_cmd_mcp_open, dpaa2_rc_mcp_open), + DEVMETHOD(dpaa2_cmd_mcp_close, dpaa2_rc_mcp_close), + DEVMETHOD(dpaa2_cmd_mcp_reset, dpaa2_rc_mcp_reset), + + DEVMETHOD_END +}; + +static driver_t dpaa2_rc_driver = { + "dpaa2_rc", + dpaa2_rc_methods, + sizeof(struct dpaa2_rc_softc), +}; + +/* For root container */ +DRIVER_MODULE(dpaa2_rc, dpaa2_mc, dpaa2_rc_driver, 0, 0); +/* For child containers */ +DRIVER_MODULE(dpaa2_rc, dpaa2_rc, dpaa2_rc_driver, 0, 0); diff --git a/sys/dev/dpaa2/dpaa2_swp.c b/sys/dev/dpaa2/dpaa2_swp.c new file mode 100644 index 000000000000..2ceed08159ef --- /dev/null +++ b/sys/dev/dpaa2/dpaa2_swp.c @@ -0,0 +1,1169 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause AND BSD-2-Clause + * + * Copyright © 2014-2016 Freescale Semiconductor, Inc. + * Copyright © 2016-2019 NXP + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Original source file obtained from: + * drivers/soc/fsl/dpio/qbman-portal.c + * + * Commit: 4c86114194e644b6da9107d75910635c9e87179e + * Repository: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git + */ + +/* + * Copyright © 2021-2022 Dmitry Salychev + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +/* + * DPAA2 QBMan software portal. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "pcib_if.h" +#include "pci_if.h" + +#include "dpaa2_swp.h" +#include "dpaa2_mc.h" +#include "dpaa2_bp.h" + +#define CMD_SPIN_TIMEOUT 100u /* us */ +#define CMD_SPIN_ATTEMPTS 2000u /* 200 ms max. */ + +#define CMD_VERB_MASK 0x7Fu + +/* Shifts in the VERB byte of the enqueue command descriptor. */ +#define ENQ_CMD_ORP_ENABLE_SHIFT 2 +#define ENQ_CMD_IRQ_ON_DISPATCH_SHIFT 3 +#define ENQ_CMD_TARGET_TYPE_SHIFT 4 +#define ENQ_CMD_DCA_EN_SHIFT 7 +/* VERB byte options of the enqueue command descriptor. */ +#define ENQ_CMD_EMPTY 0u +#define ENQ_CMD_RESPONSE_ALWAYS 1u +#define ENQ_CMD_REJECTS_TO_FQ 2u + +#define ENQ_DESC_FD_OFFSET 32u + +#define ENQ_DCA_IDXMASK 0x0Fu +#define ENQ_FLAG_DCA (1ull << 31) + +/* QBMan portal command codes. */ +#define CMDID_SWP_MC_ACQUIRE 0x30 +#define CMDID_SWP_BP_QUERY 0x32 +#define CMDID_SWP_WQCHAN_CONFIGURE 0x46 + +/* QBMan portal command result codes. */ +#define QBMAN_CMD_RC_OK 0xF0 + +/* SDQCR attribute codes */ +#define QB_SDQCR_FC_SHIFT 29u +#define QB_SDQCR_FC_MASK 0x1u +#define QB_SDQCR_DCT_SHIFT 24u +#define QB_SDQCR_DCT_MASK 0x3u +#define QB_SDQCR_TOK_SHIFT 16u +#define QB_SDQCR_TOK_MASK 0xFFu +#define QB_SDQCR_SRC_SHIFT 0u +#define QB_SDQCR_SRC_MASK 0xFFFFu + +/* Shifts in the VERB byte of the volatile dequeue command. */ +#define QB_VDQCR_VERB_DCT0_SHIFT 0 +#define QB_VDQCR_VERB_DCT1_SHIFT 1 +#define QB_VDQCR_VERB_DT0_SHIFT 2 +#define QB_VDQCR_VERB_DT1_SHIFT 3 +#define QB_VDQCR_VERB_RLS_SHIFT 4 +#define QB_VDQCR_VERB_WAE_SHIFT 5 +#define QB_VDQCR_VERB_RAD_SHIFT 6 + +/* Maximum timeout period for the DQRR interrupt. */ +#define DQRR_MAX_ITP 4096u +#define DQRR_PI_MASK 0x0Fu + +/* Release Array Allocation register helpers. */ +#define RAR_IDX(rar) ((rar) & 0x7u) +#define RAR_VB(rar) ((rar) & 0x80u) +#define RAR_SUCCESS(rar) ((rar) & 0x100u) + +MALLOC_DEFINE(M_DPAA2_SWP, "dpaa2_swp", "DPAA2 QBMan Software Portal"); + +enum qbman_sdqcr_dct { + qbman_sdqcr_dct_null = 0, + qbman_sdqcr_dct_prio_ics, + qbman_sdqcr_dct_active_ics, + qbman_sdqcr_dct_active +}; + +enum qbman_sdqcr_fc { + qbman_sdqcr_fc_one = 0, + qbman_sdqcr_fc_up_to_3 = 1 +}; + +/* Routines to execute software portal commands. */ +static int dpaa2_swp_exec_mgmt_command(struct dpaa2_swp *, + struct dpaa2_swp_cmd *, struct dpaa2_swp_rsp *, uint8_t); +static int dpaa2_swp_exec_br_command(struct dpaa2_swp *, struct dpaa2_swp_cmd *, + uint32_t); +static int dpaa2_swp_exec_vdc_command_locked(struct dpaa2_swp *, + struct dpaa2_swp_cmd *); + +/* Management Commands helpers. */ +static int dpaa2_swp_send_mgmt_command(struct dpaa2_swp *, + struct dpaa2_swp_cmd *, uint8_t); +static int dpaa2_swp_wait_for_mgmt_response(struct dpaa2_swp *, + struct dpaa2_swp_rsp *); + +/* Helper subroutines. */ +static int dpaa2_swp_cyc_diff(uint8_t, uint8_t, uint8_t); + +int +dpaa2_swp_init_portal(struct dpaa2_swp **swp, struct dpaa2_swp_desc *desc, + uint16_t flags) +{ + struct dpaa2_swp *p; + uint32_t reg, mask_size, eqcr_pi; /* EQCR producer index */ + + if (!swp || !desc) + return (DPAA2_SWP_STAT_EINVAL); + + p = malloc(sizeof(struct dpaa2_swp), M_DPAA2_SWP, + flags & DPAA2_SWP_NOWAIT_ALLOC + ? (M_NOWAIT | M_ZERO) + : (M_WAITOK | M_ZERO)); + if (!p) + return (DPAA2_SWP_STAT_NO_MEMORY); + + mtx_init(&p->lock, "swp_sleep_lock", NULL, MTX_DEF); + + p->cfg.mem_backed = false; + p->cfg.writes_cinh = true; + + p->desc = desc; + p->flags = flags; + p->mc.valid_bit = DPAA2_SWP_VALID_BIT; + p->mr.valid_bit = DPAA2_SWP_VALID_BIT; + + /* FIXME: Memory-backed mode doesn't work now. Why? */ + p->cena_res = desc->cena_res; + p->cena_map = desc->cena_map; + p->cinh_res = desc->cinh_res; + p->cinh_map = desc->cinh_map; + + /* Static Dequeue Command Register configuration. */ + p->sdq = 0; + p->sdq |= qbman_sdqcr_dct_prio_ics << QB_SDQCR_DCT_SHIFT; + p->sdq |= qbman_sdqcr_fc_up_to_3 << QB_SDQCR_FC_SHIFT; + p->sdq |= DPAA2_SWP_SDQCR_TOKEN << QB_SDQCR_TOK_SHIFT; + + /* Volatile Dequeue Command configuration. */ + p->vdq.valid_bit = DPAA2_SWP_VALID_BIT; + + /* Dequeue Response Ring configuration */ + p->dqrr.next_idx = 0; + p->dqrr.valid_bit = DPAA2_SWP_VALID_BIT; + if ((desc->swp_version & DPAA2_SWP_REV_MASK) < DPAA2_SWP_REV_4100) { + p->dqrr.ring_size = 4; + p->dqrr.reset_bug = 1; + } else { + p->dqrr.ring_size = 8; + p->dqrr.reset_bug = 0; + } + + if ((desc->swp_version & DPAA2_SWP_REV_MASK) < DPAA2_SWP_REV_5000) { + reg = dpaa2_swp_set_cfg( + p->dqrr.ring_size, /* max. entries QMan writes to DQRR */ + 1, /* writes enabled in the CINH memory only */ + 0, /* EQCR_CI stashing threshold */ + 3, /* RPM: RCR in array mode */ + 2, /* DCM: Discrete consumption ack */ + 2, /* EPM: EQCR in ring mode (FIFO) */ + 1, /* mem stashing drop enable enable */ + 1, /* mem stashing priority enable */ + 1, /* mem stashing enable */ + 1, /* dequeue stashing priority enable */ + 0, /* dequeue stashing enable enable */ + 0 /* EQCR_CI stashing priority enable */ + ); + reg &= ~(1 << DPAA2_SWP_CFG_CPBS_SHIFT); /* QMan-backed mode */ + } else { + bus_set_region_4(p->cena_map, 0, 0, + rman_get_size(p->cena_res) / 4); + + reg = dpaa2_swp_set_cfg( + p->dqrr.ring_size, /* max. entries QMan writes to DQRR */ /* DQRR_MF */ + 1, /* writes enabled in the CINH memory only */ /* WN */ + 0, /* EQCR_CI stashing is disabled */ /* EST */ + 3, /* RPM: RCR in array mode */ /* RPM */ + 2, /* DCM: Discrete consumption ack */ /* DCM */ + 2, /* EPM: EQCR in ring mode (FIFO) */ /* EPM */ + 1, /* Dequeued frame data, annotation, and FQ context stashing drop enable */ /* SD */ + 1, /* Dequeued frame data, annotation, and FQ context stashing priority */ /* SP */ + 1, /* Dequeued frame data, annotation, and FQ context stashing enable */ /* SE */ + 1, /* Dequeue response ring (DQRR) entry stashing priority */ /* DP */ + 0, /* Dequeue response ring (DQRR) entry, or cacheable portal area, stashing enable. */ /* DE */ + 0 /* EQCR_CI stashing priority */ /* EP */ + ); + /* TODO: Switch to memory-backed mode. */ + reg &= ~(1 << DPAA2_SWP_CFG_CPBS_SHIFT); /* QMan-backed mode */ + } + dpaa2_swp_write_reg(p, DPAA2_SWP_CINH_CFG, reg); + reg = dpaa2_swp_read_reg(p, DPAA2_SWP_CINH_CFG); + if (!reg) { + free(p, M_DPAA2_SWP); + return (DPAA2_SWP_STAT_PORTAL_DISABLED); + } + + /* + * Static Dequeue Command Register needs to be initialized to 0 when no + * channels are being dequeued from or else the QMan HW will indicate an + * error. The values that were calculated above will be applied when + * dequeues from a specific channel are enabled. + */ + dpaa2_swp_write_reg(p, DPAA2_SWP_CINH_SDQCR, 0); + + p->eqcr.pi_ring_size = 8; + /* if ((desc->swp_version & DPAA2_SWP_REV_MASK) >= DPAA2_SWP_REV_5000) */ + /* p->eqcr.pi_ring_size = 32; */ + + for (mask_size = p->eqcr.pi_ring_size; mask_size > 0; mask_size >>= 1) + p->eqcr.pi_ci_mask = (p->eqcr.pi_ci_mask << 1) + 1; + + eqcr_pi = dpaa2_swp_read_reg(p, DPAA2_SWP_CINH_EQCR_PI); + p->eqcr.pi = eqcr_pi & p->eqcr.pi_ci_mask; + p->eqcr.pi_vb = eqcr_pi & DPAA2_SWP_VALID_BIT; + p->eqcr.ci = dpaa2_swp_read_reg(p, DPAA2_SWP_CINH_EQCR_CI) + & p->eqcr.pi_ci_mask; + p->eqcr.available = p->eqcr.pi_ring_size; + + /* Initialize the portal with an IRQ threshold and timeout of 0us. */ + dpaa2_swp_set_irq_coalescing(p, p->dqrr.ring_size - 1, 0); + + *swp = p; + + return (0); +} + +void +dpaa2_swp_free_portal(struct dpaa2_swp *swp) +{ + uint16_t flags; + + KASSERT(swp != NULL, ("%s: swp is NULL", __func__)); + + DPAA2_SWP_LOCK(swp, &flags); + swp->flags |= DPAA2_SWP_DESTROYED; + DPAA2_SWP_UNLOCK(swp); + + /* Let threads stop using this portal. */ + DELAY(DPAA2_SWP_TIMEOUT); + + mtx_destroy(&swp->lock); + free(swp, M_DPAA2_SWP); +} + +uint32_t +dpaa2_swp_set_cfg(uint8_t max_fill, uint8_t wn, uint8_t est, uint8_t rpm, + uint8_t dcm, uint8_t epm, int sd, int sp, int se, int dp, int de, int ep) +{ + return ( + max_fill << DPAA2_SWP_CFG_DQRR_MF_SHIFT | + est << DPAA2_SWP_CFG_EST_SHIFT | + wn << DPAA2_SWP_CFG_WN_SHIFT | + rpm << DPAA2_SWP_CFG_RPM_SHIFT | + dcm << DPAA2_SWP_CFG_DCM_SHIFT | + epm << DPAA2_SWP_CFG_EPM_SHIFT | + sd << DPAA2_SWP_CFG_SD_SHIFT | + sp << DPAA2_SWP_CFG_SP_SHIFT | + se << DPAA2_SWP_CFG_SE_SHIFT | + dp << DPAA2_SWP_CFG_DP_SHIFT | + de << DPAA2_SWP_CFG_DE_SHIFT | + ep << DPAA2_SWP_CFG_EP_SHIFT + ); +} + +/* Read/write registers of a software portal. */ + +void +dpaa2_swp_write_reg(struct dpaa2_swp *swp, uint32_t o, uint32_t v) +{ + bus_write_4(swp->cinh_map, o, v); +} + +uint32_t +dpaa2_swp_read_reg(struct dpaa2_swp *swp, uint32_t o) +{ + return (bus_read_4(swp->cinh_map, o)); +} + +/* Helper routines. */ + +/** + * @brief Set enqueue descriptor without Order Point Record ID. + * + * ed: Enqueue descriptor. + * resp_always: Enqueue with response always (1); FD from a rejected enqueue + * will be returned on a FQ (0). + */ +void +dpaa2_swp_set_ed_norp(struct dpaa2_eq_desc *ed, bool resp_always) +{ + ed->verb &= ~(1 << ENQ_CMD_ORP_ENABLE_SHIFT); + if (resp_always) + ed->verb |= ENQ_CMD_RESPONSE_ALWAYS; + else + ed->verb |= ENQ_CMD_REJECTS_TO_FQ; +} + +/** + * @brief Set FQ of the enqueue descriptor. + */ +void +dpaa2_swp_set_ed_fq(struct dpaa2_eq_desc *ed, uint32_t fqid) +{ + ed->verb &= ~(1 << ENQ_CMD_TARGET_TYPE_SHIFT); + ed->tgtid = fqid; +} + +/** + * @brief Enable interrupts for a software portal. + */ +void +dpaa2_swp_set_intr_trigger(struct dpaa2_swp *swp, uint32_t mask) +{ + if (swp != NULL) + dpaa2_swp_write_reg(swp, DPAA2_SWP_CINH_IER, mask); +} + +/** + * @brief Return the value in the SWP_IER register. + */ +uint32_t +dpaa2_swp_get_intr_trigger(struct dpaa2_swp *swp) +{ + if (swp != NULL) + return dpaa2_swp_read_reg(swp, DPAA2_SWP_CINH_IER); + return (0); +} + +/** + * @brief Return the value in the SWP_ISR register. + */ +uint32_t +dpaa2_swp_read_intr_status(struct dpaa2_swp *swp) +{ + if (swp != NULL) + return dpaa2_swp_read_reg(swp, DPAA2_SWP_CINH_ISR); + return (0); +} + +/** + * @brief Clear SWP_ISR register according to the given mask. + */ +void +dpaa2_swp_clear_intr_status(struct dpaa2_swp *swp, uint32_t mask) +{ + if (swp != NULL) + dpaa2_swp_write_reg(swp, DPAA2_SWP_CINH_ISR, mask); +} + +/** + * @brief Enable or disable push dequeue. + * + * swp: the software portal object + * chan_idx: the channel index (0 to 15) + * en: enable or disable push dequeue + */ +void +dpaa2_swp_set_push_dequeue(struct dpaa2_swp *swp, uint8_t chan_idx, bool en) +{ + uint16_t dqsrc; + + if (swp != NULL) { + if (chan_idx > 15u) { + device_printf(swp->desc->dpio_dev, "channel index " + "should be <= 15: chan_idx=%d\n", chan_idx); + return; + } + + if (en) + swp->sdq |= 1 << chan_idx; + else + swp->sdq &= ~(1 << chan_idx); + /* + * Read make the complete src map. If no channels are enabled + * the SDQCR must be 0 or else QMan will assert errors. + */ + dqsrc = (swp->sdq >> DPAA2_SDQCR_SRC_SHIFT) & + DPAA2_SDQCR_SRC_MASK; + dpaa2_swp_write_reg(swp, DPAA2_SWP_CINH_SDQCR, dqsrc != 0 + ? swp->sdq : 0); + } +} + +/** + * @brief Set new IRQ coalescing values. + * + * swp: The software portal object. + * threshold: Threshold for DQRR interrupt generation. The DQRR interrupt + * asserts when the ring contains greater than "threshold" entries. + * holdoff: DQRR interrupt holdoff (timeout) period in us. + */ +int dpaa2_swp_set_irq_coalescing(struct dpaa2_swp *swp, uint32_t threshold, + uint32_t holdoff) +{ + uint32_t itp; /* Interrupt Timeout Period */ + + if (swp == NULL) + return (EINVAL); + + /* + * Convert "holdoff" value from us to 256 QBMAN clock cycles + * increments. This depends on the QBMAN internal frequency. + */ + itp = (holdoff * 1000u) / swp->desc->swp_cycles_ratio; + if (itp > DQRR_MAX_ITP) + itp = DQRR_MAX_ITP; + if (threshold >= swp->dqrr.ring_size) + threshold = swp->dqrr.ring_size - 1; + + swp->dqrr.irq_threshold = threshold; + swp->dqrr.irq_itp = itp; + + dpaa2_swp_write_reg(swp, DPAA2_SWP_CINH_DQRR_ITR, threshold); + dpaa2_swp_write_reg(swp, DPAA2_SWP_CINH_ITPR, itp); + + return (0); +} + +/* + * Software portal commands. + */ + +/** + * @brief Configure the channel data availability notification (CDAN) + * in a particular WQ channel. + */ +int +dpaa2_swp_conf_wq_channel(struct dpaa2_swp *swp, uint16_t chan_id, + uint8_t we_mask, bool cdan_en, uint64_t ctx) +{ + /* NOTE: 64 bytes command. */ + struct __packed { + uint8_t verb; + uint8_t result; /* in response only! */ + uint16_t chan_id; + uint8_t we; + uint8_t ctrl; + uint16_t _reserved2; + uint64_t ctx; + uint8_t _reserved3[48]; + } cmd = {0}; + struct __packed { + uint8_t verb; + uint8_t result; + uint16_t chan_id; + uint8_t _reserved[60]; + } rsp; + int error; + + if (swp == NULL) + return (EINVAL); + + cmd.chan_id = chan_id; + cmd.we = we_mask; + cmd.ctrl = cdan_en ? 1u : 0u; + cmd.ctx = ctx; + + error = dpaa2_swp_exec_mgmt_command(swp, (struct dpaa2_swp_cmd *) &cmd, + (struct dpaa2_swp_rsp *) &rsp, CMDID_SWP_WQCHAN_CONFIGURE); + if (error) + return (error); + + if (rsp.result != QBMAN_CMD_RC_OK) { + device_printf(swp->desc->dpio_dev, "WQ channel configuration " + "error: channel_id=%d, result=0x%02x\n", chan_id, + rsp.result); + return (EIO); + } + + return (0); +} + +/** + * @brief Query current configuration/state of the buffer pool. + */ +int +dpaa2_swp_query_bp(struct dpaa2_swp *swp, uint16_t bpid, + struct dpaa2_bp_conf *conf) +{ + /* NOTE: 64 bytes command. */ + struct __packed { + uint8_t verb; + uint8_t _reserved1; + uint16_t bpid; + uint8_t _reserved2[60]; + } cmd = {0}; + struct __packed { + uint8_t verb; + uint8_t result; + uint32_t _reserved1; + uint8_t bdi; + uint8_t state; + uint32_t fill; + /* TODO: Support the other fields as well. */ + uint8_t _reserved2[52]; + } rsp; + int error; + + if (swp == NULL || conf == NULL) + return (EINVAL); + + cmd.bpid = bpid; + + error = dpaa2_swp_exec_mgmt_command(swp, (struct dpaa2_swp_cmd *) &cmd, + (struct dpaa2_swp_rsp *) &rsp, CMDID_SWP_BP_QUERY); + if (error) + return (error); + + if (rsp.result != QBMAN_CMD_RC_OK) { + device_printf(swp->desc->dpio_dev, "BP query error: bpid=%d, " + "result=0x%02x\n", bpid, rsp.result); + return (EIO); + } + + conf->bdi = rsp.bdi; + conf->state = rsp.state; + conf->free_bufn = rsp.fill; + + return (0); +} + +int +dpaa2_swp_release_bufs(struct dpaa2_swp *swp, uint16_t bpid, bus_addr_t *buf, + uint32_t buf_num) +{ + /* NOTE: 64 bytes command. */ + struct __packed { + uint8_t verb; + uint8_t _reserved1; + uint16_t bpid; + uint32_t _reserved2; + uint64_t buf[DPAA2_SWP_BUFS_PER_CMD]; + } cmd = {0}; + int error; + + if (swp == NULL || buf == NULL || buf_num == 0u || + buf_num > DPAA2_SWP_BUFS_PER_CMD) + return (EINVAL); + + for (uint32_t i = 0; i < buf_num; i++) + cmd.buf[i] = buf[i]; + cmd.bpid = bpid; + cmd.verb |= 1 << 5; /* Switch release buffer command to valid. */ + + error = dpaa2_swp_exec_br_command(swp, (struct dpaa2_swp_cmd *) &cmd, + buf_num); + if (error) { + device_printf(swp->desc->dpio_dev, "buffers release command " + "failed\n"); + return (error); + } + + return (0); +} + +int +dpaa2_swp_dqrr_next_locked(struct dpaa2_swp *swp, struct dpaa2_dq *dq, + uint32_t *idx) +{ + struct resource_map *map = swp->cinh_map; + struct dpaa2_swp_rsp *rsp = (struct dpaa2_swp_rsp *) dq; + uint32_t verb, pi; /* producer index */ + uint32_t offset = swp->cfg.mem_backed + ? DPAA2_SWP_CENA_DQRR_MEM(swp->dqrr.next_idx) + : DPAA2_SWP_CENA_DQRR(swp->dqrr.next_idx); + + if (swp == NULL || dq == NULL) + return (EINVAL); + + /* + * Before using valid-bit to detect if something is there, we have to + * handle the case of the DQRR reset bug... + */ + if (swp->dqrr.reset_bug) { + /* + * We pick up new entries by cache-inhibited producer index, + * which means that a non-coherent mapping would require us to + * invalidate and read *only* once that PI has indicated that + * there's an entry here. The first trip around the DQRR ring + * will be much less efficient than all subsequent trips around + * it... + */ + pi = dpaa2_swp_read_reg(swp, DPAA2_SWP_CINH_DQPI) & DQRR_PI_MASK; + + /* There are new entries if pi != next_idx */ + if (pi == swp->dqrr.next_idx) + return (ENOENT); + + /* + * If next_idx is/was the last ring index, and 'pi' is + * different, we can disable the workaround as all the ring + * entries have now been DMA'd to so valid-bit checking is + * repaired. + * + * NOTE: This logic needs to be based on next_idx (which + * increments one at a time), rather than on pi (which + * can burst and wrap-around between our snapshots of it). + */ + if (swp->dqrr.next_idx == (swp->dqrr.ring_size - 1)) + swp->dqrr.reset_bug = 0; + } + + verb = bus_read_4(map, offset); + if ((verb & DPAA2_SWP_VALID_BIT) != swp->dqrr.valid_bit) + return (ENOENT); + + /* Read dequeue response message. */ + for (int i = 0; i < DPAA2_SWP_RSP_PARAMS_N; i++) + rsp->params[i] = bus_read_8(map, offset + i * sizeof(uint64_t)); + + /* Return index of the current entry (if requested). */ + if (idx != NULL) + *idx = swp->dqrr.next_idx; + + /* + * There's something there. Move "next_idx" attention to the next ring + * entry before returning what we found. + */ + swp->dqrr.next_idx++; + swp->dqrr.next_idx &= swp->dqrr.ring_size - 1; /* wrap around */ + if (swp->dqrr.next_idx == 0u) + swp->dqrr.valid_bit ^= DPAA2_SWP_VALID_BIT; + + return (0); +} + +int +dpaa2_swp_pull(struct dpaa2_swp *swp, uint16_t chan_id, struct dpaa2_buf *buf, + uint32_t frames_n) +{ + /* NOTE: 64 bytes command. */ + struct __packed { + uint8_t verb; + uint8_t numf; + uint8_t tok; + uint8_t _reserved; + uint32_t dq_src; + uint64_t rsp_addr; + uint64_t _reserved1[6]; + } cmd = {0}; + struct dpaa2_dq *msg; + uint16_t flags; + int i, error; + + KASSERT(swp != NULL, ("%s: swp is NULL", __func__)); + KASSERT(frames_n != 0u, ("%s: cannot pull zero frames", __func__)); + KASSERT(frames_n <= 16u, ("%s: too much frames to pull", __func__)); + KASSERT(buf->type == DPAA2_BUF_STORE, ("%s: not channel storage " + "buffer", __func__)); + + cmd.numf = frames_n - 1; + cmd.tok = DPAA2_SWP_VDQCR_TOKEN; + cmd.dq_src = chan_id; + cmd.rsp_addr = (uint64_t) buf->store.paddr; + + /* Dequeue command type */ + cmd.verb &= ~(1 << QB_VDQCR_VERB_DCT0_SHIFT); + cmd.verb |= (1 << QB_VDQCR_VERB_DCT1_SHIFT); + /* Dequeue from a specific software portal channel (ID's in DQ_SRC). */ + cmd.verb &= ~(1 << QB_VDQCR_VERB_DT0_SHIFT); + cmd.verb &= ~(1 << QB_VDQCR_VERB_DT1_SHIFT); + /* Write the response to this command into memory (at the RSP_ADDR). */ + cmd.verb |= (1 << QB_VDQCR_VERB_RLS_SHIFT); + /* Response writes won't attempt to allocate into a cache. */ + cmd.verb &= ~(1 << QB_VDQCR_VERB_WAE_SHIFT); + /* Allow the FQ to remain active in the portal after dequeue. */ + cmd.verb &= ~(1 << QB_VDQCR_VERB_RAD_SHIFT); + + DPAA2_SWP_LOCK(swp, &flags); + if (flags & DPAA2_SWP_DESTROYED) { + /* Terminate operation if portal is destroyed. */ + DPAA2_SWP_UNLOCK(swp); + return (ENOENT); + } + + error = dpaa2_swp_exec_vdc_command_locked(swp, + (struct dpaa2_swp_cmd *) &cmd); + if (error != 0) { + DPAA2_SWP_UNLOCK(swp); + return (error); + } + + /* Let's sync before reading VDQ response from QBMan. */ + bus_dmamap_sync(buf->store.dmat, buf->store.dmap, BUS_DMASYNC_POSTREAD); + + /* Read VDQ response from QBMan. */ + msg = (struct dpaa2_dq *) buf->store.vaddr; + for (i = 1; i <= CMD_SPIN_ATTEMPTS; i++) { + if ((msg->fdr.desc.stat & DPAA2_DQ_STAT_VOLATILE) && + (msg->fdr.desc.tok == DPAA2_SWP_VDQCR_TOKEN)) { + /* Reset token. */ + msg->fdr.desc.tok = 0; + break; + } + DELAY(CMD_SPIN_TIMEOUT); + } + DPAA2_SWP_UNLOCK(swp); + + /* Return an error on expired timeout. */ + return (i > CMD_SPIN_ATTEMPTS ? ETIMEDOUT : 0); +} + +/** + * @brief Issue a command to enqueue a frame using one enqueue descriptor. + * + * swp: Software portal used to send this command to. + * ed: Enqueue command descriptor. + * fd: Frame descriptor to enqueue. + */ +int +dpaa2_swp_enq(struct dpaa2_swp *swp, struct dpaa2_eq_desc *ed, + struct dpaa2_fd *fd) +{ + uint32_t flags = 0; + int rc = dpaa2_swp_enq_mult(swp, ed, fd, &flags, 1); + + return (rc >= 0 ? 0 : EBUSY); +} + +/** + * @brief Issue a command to enqueue frames using one enqueue descriptor. + * + * swp: Software portal used to send this command to. + * ed: Enqueue command descriptor. + * fd: Frame descriptor to enqueue. + * flags: Table pointer of QBMAN_ENQUEUE_FLAG_DCA flags, not used if NULL. + * frames_n: Number of FDs to enqueue. + * + * NOTE: Enqueue command (64 bytes): 32 (eq. descriptor) + 32 (frame descriptor). + */ +int +dpaa2_swp_enq_mult(struct dpaa2_swp *swp, struct dpaa2_eq_desc *ed, + struct dpaa2_fd *fd, uint32_t *flags, int frames_n) +{ + const uint8_t *ed_pdat8 = (const uint8_t *) ed; + const uint32_t *ed_pdat32 = (const uint32_t *) ed; + const uint64_t *ed_pdat64 = (const uint64_t *) ed; + const uint64_t *fd_pdat64 = (const uint64_t *) fd; + struct resource_map *map; + uint32_t eqcr_ci, eqcr_pi; /* EQCR consumer/producer index */ + uint32_t half_mask, full_mask, val, ci_offset; + uint16_t swp_flags; + int num_enq = 0; + + if (swp == NULL || ed == NULL || fd == NULL || flags == NULL || + frames_n == 0) + return (EINVAL); + + DPAA2_SWP_LOCK(swp, &swp_flags); + if (swp_flags & DPAA2_SWP_DESTROYED) { + /* Terminate operation if portal is destroyed. */ + DPAA2_SWP_UNLOCK(swp); + return (ENOENT); + } + + map = swp->cfg.writes_cinh ? swp->cinh_map : swp->cena_map; + ci_offset = swp->cfg.mem_backed + ? DPAA2_SWP_CENA_EQCR_CI_MEMBACK + : DPAA2_SWP_CENA_EQCR_CI; + + half_mask = swp->eqcr.pi_ci_mask >> 1; + full_mask = swp->eqcr.pi_ci_mask; + + if (swp->eqcr.available == 0) { + val = dpaa2_swp_read_reg(swp, ci_offset); + eqcr_ci = swp->eqcr.ci; + swp->eqcr.ci = val & full_mask; + + swp->eqcr.available = dpaa2_swp_cyc_diff(swp->eqcr.pi_ring_size, + eqcr_ci, swp->eqcr.ci); + + if (swp->eqcr.available == 0) { + DPAA2_SWP_UNLOCK(swp); + return (0); + } + } + + eqcr_pi = swp->eqcr.pi; + num_enq = swp->eqcr.available < frames_n + ? swp->eqcr.available : frames_n; + swp->eqcr.available -= num_enq; + + KASSERT(num_enq >= 0 && num_enq <= swp->eqcr.pi_ring_size, + ("%s: unexpected num_enq=%d", __func__, num_enq)); + KASSERT(swp->eqcr.available >= 0 && + swp->eqcr.available <= swp->eqcr.pi_ring_size, + ("%s: unexpected eqcr.available=%d", __func__, swp->eqcr.available)); + + /* Fill in the EQCR ring. */ + for (int i = 0; i < num_enq; i++) { + /* Write enq. desc. without the VERB, DCA, SEQNUM and OPRID. */ + for (int j = 1; j <= 3; j++) + bus_write_8(map, + DPAA2_SWP_CENA_EQCR(eqcr_pi & half_mask) + + sizeof(uint64_t) * j, ed_pdat64[j]); + /* Write OPRID. */ + bus_write_4(map, + DPAA2_SWP_CENA_EQCR(eqcr_pi & half_mask) + sizeof(uint32_t), + ed_pdat32[1]); + /* Write DCA and SEQNUM without VERB byte. */ + for (int j = 1; j <= 3; j++) + bus_write_1(map, + DPAA2_SWP_CENA_EQCR(eqcr_pi & half_mask) + + sizeof(uint8_t) * j, ed_pdat8[j]); + + /* Write frame descriptor. */ + for (int j = 0; j <= 3; j++) + bus_write_8(map, + DPAA2_SWP_CENA_EQCR(eqcr_pi & half_mask) + + ENQ_DESC_FD_OFFSET + + sizeof(uint64_t) * j, fd_pdat64[j]); + eqcr_pi++; + } + + wmb(); + + /* Write the VERB byte of enqueue descriptor. */ + eqcr_pi = swp->eqcr.pi; + for (int i = 0; i < num_enq; i++) { + bus_write_1(map, + DPAA2_SWP_CENA_EQCR(eqcr_pi & half_mask), + ed_pdat8[0] | swp->eqcr.pi_vb); + + if (flags && (flags[i] & ENQ_FLAG_DCA)) { + /* Update DCA byte. */ + bus_write_1(map, + DPAA2_SWP_CENA_EQCR(eqcr_pi & half_mask) + 1, + (1 << ENQ_CMD_DCA_EN_SHIFT) | + (flags[i] & ENQ_DCA_IDXMASK)); + } + eqcr_pi++; + if (!(eqcr_pi & half_mask)) + swp->eqcr.pi_vb ^= DPAA2_SWP_VALID_BIT; + } + swp->eqcr.pi = eqcr_pi & full_mask; + + DPAA2_SWP_UNLOCK(swp); + + return (num_enq); +} + +static int +dpaa2_swp_cyc_diff(uint8_t ringsize, uint8_t first, uint8_t last) +{ + /* 'first' is included, 'last' is excluded */ + return ((first <= last) + ? (last - first) : ((2 * ringsize) - (first - last))); +} + +/** + * @brief Execute Buffer Release Command (BRC). + */ +static int +dpaa2_swp_exec_br_command(struct dpaa2_swp *swp, struct dpaa2_swp_cmd *cmd, + uint32_t buf_num) +{ + struct __packed with_verb { + uint8_t verb; + uint8_t _reserved[63]; + } *c; + const uint8_t *cmd_pdat8 = (const uint8_t *) cmd->params; + const uint32_t *cmd_pdat32 = (const uint32_t *) cmd->params; + struct resource_map *map; + uint32_t offset, rar; /* Release Array Allocation register */ + uint16_t flags; + + if (!swp || !cmd) + return (EINVAL); + + DPAA2_SWP_LOCK(swp, &flags); + if (flags & DPAA2_SWP_DESTROYED) { + /* Terminate operation if portal is destroyed. */ + DPAA2_SWP_UNLOCK(swp); + return (ENOENT); + } + + rar = dpaa2_swp_read_reg(swp, DPAA2_SWP_CINH_RAR); + if (!RAR_SUCCESS(rar)) { + DPAA2_SWP_UNLOCK(swp); + return (EBUSY); + } + + map = swp->cfg.writes_cinh ? swp->cinh_map : swp->cena_map; + offset = swp->cfg.mem_backed + ? DPAA2_SWP_CENA_RCR_MEM(RAR_IDX(rar)) + : DPAA2_SWP_CENA_RCR(RAR_IDX(rar)); + c = (struct with_verb *) cmd; + + /* Write command bytes (without VERB byte). */ + for (uint32_t i = 1; i < DPAA2_SWP_CMD_PARAMS_N; i++) + bus_write_8(map, offset + sizeof(uint64_t) * i, cmd->params[i]); + bus_write_4(map, offset + 4, cmd_pdat32[1]); + for (uint32_t i = 1; i <= 3; i++) + bus_write_1(map, offset + i, cmd_pdat8[i]); + + /* Write VERB byte and trigger command execution. */ + if (swp->cfg.mem_backed) { + bus_write_1(map, offset, c->verb | RAR_VB(rar) | buf_num); + wmb(); + dpaa2_swp_write_reg(swp, DPAA2_SWP_CINH_RCR_AM_RT + + RAR_IDX(rar) * 4, DPAA2_SWP_RT_MODE); + } else { + wmb(); + bus_write_1(map, offset, c->verb | RAR_VB(rar) | buf_num); + } + + DPAA2_SWP_UNLOCK(swp); + + return (0); +} + +/** + * @brief Execute Volatile Dequeue Command (VDC). + * + * This command will be executed by QBMan only once in order to deliver requested + * number of frames (1-16 or 1-32 depending on QBMan version) to the driver via + * DQRR or arbitrary DMA-mapped memory. + * + * NOTE: There is a counterpart to the volatile dequeue command called static + * dequeue command (SDQC) which is executed periodically all the time the + * command is present in the SDQCR register. + */ +static int +dpaa2_swp_exec_vdc_command_locked(struct dpaa2_swp *swp, + struct dpaa2_swp_cmd *cmd) +{ + struct __packed with_verb { + uint8_t verb; + uint8_t _reserved[63]; + } *c; + const uint8_t *p8 = (const uint8_t *) cmd->params; + const uint32_t *p32 = (const uint32_t *) cmd->params; + struct resource_map *map; + uint32_t offset; + + map = swp->cfg.writes_cinh ? swp->cinh_map : swp->cena_map; + offset = swp->cfg.mem_backed + ? DPAA2_SWP_CENA_VDQCR_MEM : DPAA2_SWP_CENA_VDQCR; + c = (struct with_verb *) cmd; + + /* Write command bytes (without VERB byte). */ + for (uint32_t i = 1; i < DPAA2_SWP_CMD_PARAMS_N; i++) + bus_write_8(map, offset + sizeof(uint64_t) * i, cmd->params[i]); + bus_write_4(map, offset + 4, p32[1]); + for (uint32_t i = 1; i <= 3; i++) + bus_write_1(map, offset + i, p8[i]); + + /* Write VERB byte and trigger command execution. */ + if (swp->cfg.mem_backed) { + bus_write_1(map, offset, c->verb | swp->vdq.valid_bit); + swp->vdq.valid_bit ^= DPAA2_SWP_VALID_BIT; + wmb(); + dpaa2_swp_write_reg(swp, DPAA2_SWP_CINH_VDQCR_RT, + DPAA2_SWP_RT_MODE); + } else { + wmb(); + bus_write_1(map, offset, c->verb | swp->vdq.valid_bit); + swp->vdq.valid_bit ^= DPAA2_SWP_VALID_BIT; + } + + return (0); +} + +/** + * @brief Execute a QBMan management command. + */ +static int +dpaa2_swp_exec_mgmt_command(struct dpaa2_swp *swp, struct dpaa2_swp_cmd *cmd, + struct dpaa2_swp_rsp *rsp, uint8_t cmdid) +{ +#if (defined(_KERNEL) && defined(INVARIANTS)) + struct __packed with_verb { + uint8_t verb; + uint8_t _reserved[63]; + } *r; +#endif + uint16_t flags; + int error; + + if (swp == NULL || cmd == NULL || rsp == NULL) + return (EINVAL); + + DPAA2_SWP_LOCK(swp, &flags); + if (flags & DPAA2_SWP_DESTROYED) { + /* Terminate operation if portal is destroyed. */ + DPAA2_SWP_UNLOCK(swp); + return (ENOENT); + } + + /* + * Send a command to QBMan using Management Command register and wait + * for response from the Management Response registers. + */ + dpaa2_swp_send_mgmt_command(swp, cmd, cmdid); + error = dpaa2_swp_wait_for_mgmt_response(swp, rsp); + if (error) { + DPAA2_SWP_UNLOCK(swp); + return (error); + } + DPAA2_SWP_UNLOCK(swp); + +#if (defined(_KERNEL) && defined(INVARIANTS)) + r = (struct with_verb *) rsp; + KASSERT((r->verb & CMD_VERB_MASK) == cmdid, + ("wrong VERB byte in response: resp=0x%02x, expected=0x%02x", + r->verb, cmdid)); +#endif + + return (0); +} + +static int +dpaa2_swp_send_mgmt_command(struct dpaa2_swp *swp, struct dpaa2_swp_cmd *cmd, + uint8_t cmdid) +{ + const uint8_t *cmd_pdat8 = (const uint8_t *) cmd->params; + const uint32_t *cmd_pdat32 = (const uint32_t *) cmd->params; + struct resource_map *map; + uint32_t offset; + + map = swp->cfg.writes_cinh ? swp->cinh_map : swp->cena_map; + offset = swp->cfg.mem_backed ? DPAA2_SWP_CENA_CR_MEM : DPAA2_SWP_CENA_CR; + + /* Write command bytes (without VERB byte). */ + for (uint32_t i = 1; i < DPAA2_SWP_CMD_PARAMS_N; i++) + bus_write_8(map, offset + sizeof(uint64_t) * i, cmd->params[i]); + bus_write_4(map, offset + 4, cmd_pdat32[1]); + for (uint32_t i = 1; i <= 3; i++) + bus_write_1(map, offset + i, cmd_pdat8[i]); + + /* Write VERB byte and trigger command execution. */ + if (swp->cfg.mem_backed) { + bus_write_1(map, offset, cmdid | swp->mr.valid_bit); + wmb(); + dpaa2_swp_write_reg(swp, DPAA2_SWP_CINH_CR_RT, + DPAA2_SWP_RT_MODE); + } else { + wmb(); + bus_write_1(map, offset, cmdid | swp->mc.valid_bit); + } + + return (0); +} + +static int +dpaa2_swp_wait_for_mgmt_response(struct dpaa2_swp *swp, struct dpaa2_swp_rsp *rsp) +{ + struct resource_map *map = swp->cfg.mem_backed + ? swp->cena_map : swp->cinh_map; + /* Management command response to be read from the only RR or RR0/RR1. */ + const uint32_t offset = swp->cfg.mem_backed + ? DPAA2_SWP_CENA_RR_MEM + : DPAA2_SWP_CENA_RR(swp->mc.valid_bit); + uint32_t i, verb, ret; + int rc; + + /* Wait for a command response from QBMan. */ + for (i = 1; i <= CMD_SPIN_ATTEMPTS; i++) { + if (swp->cfg.mem_backed) { + verb = (uint32_t) (bus_read_4(map, offset) & 0xFFu); + if (swp->mr.valid_bit != (verb & DPAA2_SWP_VALID_BIT)) + goto wait; + if (!(verb & ~DPAA2_SWP_VALID_BIT)) + goto wait; + swp->mr.valid_bit ^= DPAA2_SWP_VALID_BIT; + } else { + ret = bus_read_4(map, offset); + verb = ret & ~DPAA2_SWP_VALID_BIT; /* remove valid bit */ + if (verb == 0u) + goto wait; + swp->mc.valid_bit ^= DPAA2_SWP_VALID_BIT; + } + break; + wait: + DELAY(CMD_SPIN_TIMEOUT); + } + /* Return an error on expired timeout. */ + rc = i > CMD_SPIN_ATTEMPTS ? ETIMEDOUT : 0; + + /* Read command response. */ + for (i = 0; i < DPAA2_SWP_RSP_PARAMS_N; i++) + rsp->params[i] = bus_read_8(map, offset + i * sizeof(uint64_t)); + + return (rc); +} diff --git a/sys/dev/dpaa2/dpaa2_swp.h b/sys/dev/dpaa2/dpaa2_swp.h new file mode 100644 index 000000000000..53a8b32ef185 --- /dev/null +++ b/sys/dev/dpaa2/dpaa2_swp.h @@ -0,0 +1,504 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright © 2021-2022 Dmitry Salychev + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _DPAA2_SWP_H +#define _DPAA2_SWP_H + +#include + +#include "dpaa2_types.h" +#include "dpaa2_bp.h" + +/* + * DPAA2 QBMan software portal. + */ + +/* All QBMan commands and result structures use this "valid bit" encoding. */ +#define DPAA2_SWP_VALID_BIT ((uint32_t) 0x80) + +#define DPAA2_SWP_TIMEOUT 100000 /* in us */ +#define DPAA2_SWP_CMD_PARAMS_N 8u +#define DPAA2_SWP_RSP_PARAMS_N 8u + +/* + * Maximum number of buffers that can be acquired/released through a single + * QBMan command. + */ +#define DPAA2_SWP_BUFS_PER_CMD 7u + +/* + * Number of times to retry DPIO portal operations while waiting for portal to + * finish executing current command and become available. + * + * We want to avoid being stuck in a while loop in case hardware becomes + * unresponsive, but not give up too easily if the portal really is busy for + * valid reasons. + */ +#define DPAA2_SWP_BUSY_RETRIES 1000 + +/* Versions of the QBMan software portals. */ +#define DPAA2_SWP_REV_4000 0x04000000 +#define DPAA2_SWP_REV_4100 0x04010000 +#define DPAA2_SWP_REV_4101 0x04010001 +#define DPAA2_SWP_REV_5000 0x05000000 + +#define DPAA2_SWP_REV_MASK 0xFFFF0000 + +/* Registers in the cache-inhibited area of the software portal. */ +#define DPAA2_SWP_CINH_CR 0x600 /* Management Command reg.*/ +#define DPAA2_SWP_CINH_EQCR_PI 0x800 /* Enqueue Ring, Producer Index */ +#define DPAA2_SWP_CINH_EQCR_CI 0x840 /* Enqueue Ring, Consumer Index */ +#define DPAA2_SWP_CINH_CR_RT 0x900 /* CR Read Trigger */ +#define DPAA2_SWP_CINH_VDQCR_RT 0x940 /* VDQCR Read Trigger */ +#define DPAA2_SWP_CINH_EQCR_AM_RT 0x980 +#define DPAA2_SWP_CINH_RCR_AM_RT 0x9C0 +#define DPAA2_SWP_CINH_DQPI 0xA00 /* DQRR Producer Index reg. */ +#define DPAA2_SWP_CINH_DQRR_ITR 0xA80 /* DQRR interrupt timeout reg. */ +#define DPAA2_SWP_CINH_DCAP 0xAC0 /* DQRR Consumption Ack. reg. */ +#define DPAA2_SWP_CINH_SDQCR 0xB00 /* Static Dequeue Command reg. */ +#define DPAA2_SWP_CINH_EQCR_AM_RT2 0xB40 +#define DPAA2_SWP_CINH_RCR_PI 0xC00 /* Release Ring, Producer Index */ +#define DPAA2_SWP_CINH_RAR 0xCC0 /* Release Array Allocation reg. */ +#define DPAA2_SWP_CINH_CFG 0xD00 +#define DPAA2_SWP_CINH_ISR 0xE00 +#define DPAA2_SWP_CINH_IER 0xE40 +#define DPAA2_SWP_CINH_ISDR 0xE80 +#define DPAA2_SWP_CINH_IIR 0xEC0 +#define DPAA2_SWP_CINH_ITPR 0xF40 + +/* Registers in the cache-enabled area of the software portal. */ +#define DPAA2_SWP_CENA_EQCR(n) (0x000 + ((uint32_t)(n) << 6)) +#define DPAA2_SWP_CENA_DQRR(n) (0x200 + ((uint32_t)(n) << 6)) +#define DPAA2_SWP_CENA_RCR(n) (0x400 + ((uint32_t)(n) << 6)) +#define DPAA2_SWP_CENA_CR (0x600) /* Management Command reg. */ +#define DPAA2_SWP_CENA_RR(vb) (0x700 + ((uint32_t)(vb) >> 1)) +#define DPAA2_SWP_CENA_VDQCR (0x780) +#define DPAA2_SWP_CENA_EQCR_CI (0x840) + +/* Registers in the cache-enabled area of the software portal (memory-backed). */ +#define DPAA2_SWP_CENA_DQRR_MEM(n) (0x0800 + ((uint32_t)(n) << 6)) +#define DPAA2_SWP_CENA_RCR_MEM(n) (0x1400 + ((uint32_t)(n) << 6)) +#define DPAA2_SWP_CENA_CR_MEM (0x1600) /* Management Command reg. */ +#define DPAA2_SWP_CENA_RR_MEM (0x1680) /* Management Response reg. */ +#define DPAA2_SWP_CENA_VDQCR_MEM (0x1780) +#define DPAA2_SWP_CENA_EQCR_CI_MEMBACK (0x1840) + +/* Shifts in the portal's configuration register. */ +#define DPAA2_SWP_CFG_DQRR_MF_SHIFT 20 +#define DPAA2_SWP_CFG_EST_SHIFT 16 +#define DPAA2_SWP_CFG_CPBS_SHIFT 15 +#define DPAA2_SWP_CFG_WN_SHIFT 14 +#define DPAA2_SWP_CFG_RPM_SHIFT 12 +#define DPAA2_SWP_CFG_DCM_SHIFT 10 +#define DPAA2_SWP_CFG_EPM_SHIFT 8 +#define DPAA2_SWP_CFG_VPM_SHIFT 7 +#define DPAA2_SWP_CFG_CPM_SHIFT 6 +#define DPAA2_SWP_CFG_SD_SHIFT 5 +#define DPAA2_SWP_CFG_SP_SHIFT 4 +#define DPAA2_SWP_CFG_SE_SHIFT 3 +#define DPAA2_SWP_CFG_DP_SHIFT 2 +#define DPAA2_SWP_CFG_DE_SHIFT 1 +#define DPAA2_SWP_CFG_EP_SHIFT 0 + +/* Static Dequeue Command Register attribute codes */ +#define DPAA2_SDQCR_FC_SHIFT 29 /* Dequeue Command Frame Count */ +#define DPAA2_SDQCR_FC_MASK 0x1 +#define DPAA2_SDQCR_DCT_SHIFT 24 /* Dequeue Command Type */ +#define DPAA2_SDQCR_DCT_MASK 0x3 +#define DPAA2_SDQCR_TOK_SHIFT 16 /* Dequeue Command Token */ +#define DPAA2_SDQCR_TOK_MASK 0xff +#define DPAA2_SDQCR_SRC_SHIFT 0 /* Dequeue Source */ +#define DPAA2_SDQCR_SRC_MASK 0xffff + +/* + * Read trigger bit is used to trigger QMan to read a command from memory, + * without having software perform a cache flush to force a write of the command + * to QMan. + * + * NOTE: Implemented in QBMan 5.0 or above. + */ +#define DPAA2_SWP_RT_MODE ((uint32_t)0x100) + +/* Interrupt Enable Register bits. */ +#define DPAA2_SWP_INTR_EQRI 0x01 +#define DPAA2_SWP_INTR_EQDI 0x02 +#define DPAA2_SWP_INTR_DQRI 0x04 +#define DPAA2_SWP_INTR_RCRI 0x08 +#define DPAA2_SWP_INTR_RCDI 0x10 +#define DPAA2_SWP_INTR_VDCI 0x20 + +/* "Write Enable" bitmask for a command to configure SWP WQ Channel.*/ +#define DPAA2_WQCHAN_WE_EN (0x1u) /* Enable CDAN generation */ +#define DPAA2_WQCHAN_WE_ICD (0x2u) /* Interrupt Coalescing Disable */ +#define DPAA2_WQCHAN_WE_CTX (0x4u) + +/* Definitions for parsing DQRR entries. */ +#define DPAA2_DQRR_RESULT_MASK (0x7Fu) +#define DPAA2_DQRR_RESULT_DQ (0x60u) +#define DPAA2_DQRR_RESULT_FQRN (0x21u) +#define DPAA2_DQRR_RESULT_FQRNI (0x22u) +#define DPAA2_DQRR_RESULT_FQPN (0x24u) +#define DPAA2_DQRR_RESULT_FQDAN (0x25u) +#define DPAA2_DQRR_RESULT_CDAN (0x26u) +#define DPAA2_DQRR_RESULT_CSCN_MEM (0x27u) +#define DPAA2_DQRR_RESULT_CGCU (0x28u) +#define DPAA2_DQRR_RESULT_BPSCN (0x29u) +#define DPAA2_DQRR_RESULT_CSCN_WQ (0x2au) + +/* Frame dequeue statuses */ +#define DPAA2_DQ_STAT_FQEMPTY (0x80u) /* FQ is empty */ +#define DPAA2_DQ_STAT_HELDACTIVE (0x40u) /* FQ is held active */ +#define DPAA2_DQ_STAT_FORCEELIGIBLE (0x20u) /* FQ force eligible */ +#define DPAA2_DQ_STAT_VALIDFRAME (0x10u) /* valid frame */ +#define DPAA2_DQ_STAT_ODPVALID (0x04u) /* FQ ODP enable */ +#define DPAA2_DQ_STAT_VOLATILE (0x02u) /* volatile dequeue (VDC) */ +#define DPAA2_DQ_STAT_EXPIRED (0x01u) /* VDC is expired */ + +/* + * Portal flags. + * + * TODO: Use the same flags for both MC and software portals. + */ +#define DPAA2_SWP_DEF 0x0u +#define DPAA2_SWP_NOWAIT_ALLOC 0x2u /* Do not sleep during init */ +#define DPAA2_SWP_LOCKED 0x4000u /* Wait till portal's unlocked */ +#define DPAA2_SWP_DESTROYED 0x8000u /* Terminate any operations */ + +/* Command return codes. */ +#define DPAA2_SWP_STAT_OK 0x0 +#define DPAA2_SWP_STAT_NO_MEMORY 0x9 /* No memory available */ +#define DPAA2_SWP_STAT_PORTAL_DISABLED 0xFD /* QBMan portal disabled */ +#define DPAA2_SWP_STAT_EINVAL 0xFE /* Invalid argument */ +#define DPAA2_SWP_STAT_ERR 0xFF /* General error */ + +/* Opaque token for static dequeues. */ +#define DPAA2_SWP_SDQCR_TOKEN 0xBBu +/* Opaque token for static dequeues. */ +#define DPAA2_SWP_VDQCR_TOKEN 0xCCu + +#define DPAA2_SWP_LOCK(__swp, __flags) do { \ + mtx_assert(&(__swp)->lock, MA_NOTOWNED); \ + mtx_lock(&(__swp)->lock); \ + *(__flags) = (__swp)->flags; \ + (__swp)->flags |= DPAA2_SWP_LOCKED; \ +} while (0) + +#define DPAA2_SWP_UNLOCK(__swp) do { \ + mtx_assert(&(__swp)->lock, MA_OWNED); \ + (__swp)->flags &= ~DPAA2_SWP_LOCKED; \ + mtx_unlock(&(__swp)->lock); \ +} while (0) + +enum dpaa2_fd_format { + DPAA2_FD_SINGLE = 0, + DPAA2_FD_LIST, + DPAA2_FD_SG +}; + +/** + * @brief Enqueue command descriptor. + * + * NOTE: 32 bytes. + */ +struct dpaa2_eq_desc { + uint8_t verb; + uint8_t dca; + uint16_t seqnum; + uint16_t orpid; + uint16_t _reserved; + uint32_t tgtid; + uint32_t tag; + uint16_t qdbin; + uint8_t qpri; + uint8_t _reserved1[3]; + uint8_t wae; + uint8_t rspid; + uint64_t rsp_addr; +} __packed; + +/** + * @brief Frame Dequeue Response (FDR) descriptor. + * + * NOTE: 32 bytes. + */ +struct dpaa2_fdr_desc { + uint8_t verb; + uint8_t stat; + uint16_t seqnum; + uint16_t oprid; + uint8_t _reserved; + uint8_t tok; + uint32_t fqid; + uint32_t _reserved1; + uint32_t fq_byte_cnt; + uint32_t fq_frm_cnt; + uint64_t fqd_ctx; +} __packed; + +/** + * @brief State Change Notification Message (SCNM). + * + * NOTE: 16 bytes. + */ +struct dpaa2_scn { + uint8_t verb; + uint8_t stat; + uint8_t state; + uint8_t _reserved; + uint32_t rid_tok; + uint64_t ctx; +} __packed; + +/** + * @brief DPAA2 frame descriptor. + * + * addr: Memory address of the start of the buffer holding the + * frame data or the buffer containing the scatter/gather + * list. + * data_length: Length of the frame data (in bytes). + * bpid_ivp_bmt: Buffer pool ID (14 bit + BMT bit + IVP bit) + * offset_fmt_sl: Frame data offset, frame format and short-length fields. + * frame_ctx: Frame context. This field allows the sender of a frame + * to communicate some out-of-band information to the + * receiver of the frame. + * ctrl: Control bits (ERR, CBMT, ASAL, PTAC, DROPP, SC, DD). + * flow_ctx: Frame flow context. Associates the frame with a flow + * structure. QMan may use the FLC field for 3 purposes: + * stashing control, order definition point identification, + * and enqueue replication control. + * + * NOTE: 32 bytes. + */ +struct dpaa2_fd { + uint64_t addr; + uint32_t data_length; + uint16_t bpid_ivp_bmt; + uint16_t offset_fmt_sl; + uint32_t frame_ctx; + uint32_t ctrl; + uint64_t flow_ctx; +} __packed; + +/** + * @brief DPAA2 scatter/gather entry. + * + * NOTE: 16 bytes. + */ +struct dpaa2_sg_entry { + uint64_t addr; + uint32_t len; + uint16_t bpid; + uint16_t offset_fmt; +} __packed; + +/** + * @brief Frame Dequeue Response (FDR). + * + * NOTE: 64 bytes. + */ +struct dpaa2_fdr { + struct dpaa2_fdr_desc desc; + struct dpaa2_fd fd; +} __packed; + +/** + * @brief Dequeue Response Message. + * + * NOTE: 64 bytes. + */ +struct dpaa2_dq { + union { + struct { + uint8_t verb; + uint8_t _reserved[63]; + } common; + struct dpaa2_fdr fdr; /* Frame Dequeue Response */ + struct dpaa2_scn scn; /* State Change Notification */ + }; +} __packed; + +/** + * @brief Descriptor of the QBMan software portal. + * + * cena_res: Unmapped cache-enabled part of the portal's I/O memory. + * cena_map: Mapped cache-enabled part of the portal's I/O memory. + * cinh_res: Unmapped cache-inhibited part of the portal's I/O memory. + * cinh_map: Mapped cache-inhibited part of the portal's I/O memory. + * + * dpio_dev: Device associated with the DPIO object to manage this + * portal. + * swp_version: Hardware IP version of the software portal. + * swp_clk: QBMAN clock frequency value in Hz. + * swp_cycles_ratio: How many 256 QBMAN cycles fit into one ns. + * swp_id: Software portal ID. + * + * has_notif: True if the notification mode is used. + * has_8prio: True for a channel with 8 priority WQs. Ignored unless + * "has_notif" is true. + */ +struct dpaa2_swp_desc { + struct resource *cena_res; + struct resource_map *cena_map; + struct resource *cinh_res; + struct resource_map *cinh_map; + + device_t dpio_dev; + uint32_t swp_version; + uint32_t swp_clk; + uint32_t swp_cycles_ratio; + uint16_t swp_id; + + bool has_notif; + bool has_8prio; +}; + +/** + * @brief Command holds data to be written to the software portal. + */ +struct dpaa2_swp_cmd { + uint64_t params[DPAA2_SWP_CMD_PARAMS_N]; +}; + +/** + * @brief Command response holds data received from the software portal. + */ +struct dpaa2_swp_rsp { + uint64_t params[DPAA2_SWP_RSP_PARAMS_N]; +}; + +/** + * @brief QBMan software portal. + * + * res: Unmapped cache-enabled and cache-inhibited parts of the portal. + * map: Mapped cache-enabled and cache-inhibited parts of the portal. + * desc: Descriptor of the QBMan software portal. + * lock: Lock to guard an access to the portal. + * cv: Conditional variable helps to wait for the helper object's state + * change. + * flags: Current state of the object. + * sdq: Push dequeues status. + * mc: Management commands data. + * mr: Management response data. + * dqrr: Dequeue Response Ring is used to issue frame dequeue responses + * from the QBMan to the driver. + * eqcr: Enqueue Command Ring is used to issue frame enqueue commands + * from the driver to the QBMan. + */ +struct dpaa2_swp { + struct resource *cena_res; + struct resource_map *cena_map; + struct resource *cinh_res; + struct resource_map *cinh_map; + + struct mtx lock; + struct dpaa2_swp_desc *desc; + uint16_t flags; + + /* Static Dequeue Command Register value (to obtain CDANs). */ + uint32_t sdq; + + /* Volatile Dequeue Command (to obtain frames). */ + struct { + uint32_t valid_bit; /* 0x00 or 0x80 */ + } vdq; + + struct { + bool atomic; + bool writes_cinh; + bool mem_backed; + } cfg; /* Software portal configuration. */ + + struct { + uint32_t valid_bit; /* 0x00 or 0x80 */ + } mc; + + struct { + uint32_t valid_bit; /* 0x00 or 0x80 */ + } mr; + + struct { + uint32_t next_idx; + uint32_t valid_bit; + uint8_t ring_size; + bool reset_bug; /* dqrr reset workaround */ + uint32_t irq_threshold; + uint32_t irq_itp; + } dqrr; + + struct { + uint32_t pi; /* producer index */ + uint32_t pi_vb; /* PI valid bits */ + uint32_t pi_ring_size; + uint32_t pi_ci_mask; + uint32_t ci; + int available; + uint32_t pend; + uint32_t no_pfdr; + } eqcr; +}; + +/* Management routines. */ +int dpaa2_swp_init_portal(struct dpaa2_swp **swp, struct dpaa2_swp_desc *desc, + uint16_t flags); +void dpaa2_swp_free_portal(struct dpaa2_swp *swp); +uint32_t dpaa2_swp_set_cfg(uint8_t max_fill, uint8_t wn, uint8_t est, + uint8_t rpm, uint8_t dcm, uint8_t epm, int sd, int sp, int se, int dp, + int de, int ep); + +/* Read/write registers of a software portal. */ +void dpaa2_swp_write_reg(struct dpaa2_swp *swp, uint32_t o, uint32_t v); +uint32_t dpaa2_swp_read_reg(struct dpaa2_swp *swp, uint32_t o); + +/* Helper routines. */ +void dpaa2_swp_set_ed_norp(struct dpaa2_eq_desc *ed, bool resp_always); +void dpaa2_swp_set_ed_fq(struct dpaa2_eq_desc *ed, uint32_t fqid); +void dpaa2_swp_set_intr_trigger(struct dpaa2_swp *swp, uint32_t mask); +uint32_t dpaa2_swp_get_intr_trigger(struct dpaa2_swp *swp); +uint32_t dpaa2_swp_read_intr_status(struct dpaa2_swp *swp); +void dpaa2_swp_clear_intr_status(struct dpaa2_swp *swp, uint32_t mask); +void dpaa2_swp_set_push_dequeue(struct dpaa2_swp *swp, uint8_t chan_idx, + bool en); +int dpaa2_swp_set_irq_coalescing(struct dpaa2_swp *swp, uint32_t threshold, + uint32_t holdoff); + +/* Software portal commands. */ +int dpaa2_swp_conf_wq_channel(struct dpaa2_swp *swp, uint16_t chan_id, + uint8_t we_mask, bool cdan_en, uint64_t ctx); +int dpaa2_swp_query_bp(struct dpaa2_swp *swp, uint16_t bpid, + struct dpaa2_bp_conf *conf); +int dpaa2_swp_release_bufs(struct dpaa2_swp *swp, uint16_t bpid, bus_addr_t *buf, + uint32_t buf_num); +int dpaa2_swp_dqrr_next_locked(struct dpaa2_swp *swp, struct dpaa2_dq *dq, + uint32_t *idx); +int dpaa2_swp_pull(struct dpaa2_swp *swp, uint16_t chan_id, + struct dpaa2_buf *buf, uint32_t frames_n); +int dpaa2_swp_enq(struct dpaa2_swp *swp, struct dpaa2_eq_desc *ed, + struct dpaa2_fd *fd); +int dpaa2_swp_enq_mult(struct dpaa2_swp *swp, struct dpaa2_eq_desc *ed, + struct dpaa2_fd *fd, uint32_t *flags, int frames_n); + +#endif /* _DPAA2_SWP_H */ diff --git a/sys/dev/dpaa2/dpaa2_swp_if.m b/sys/dev/dpaa2/dpaa2_swp_if.m new file mode 100644 index 000000000000..09c658eb04d0 --- /dev/null +++ b/sys/dev/dpaa2/dpaa2_swp_if.m @@ -0,0 +1,96 @@ +#- +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright © 2021-2022 Dmitry Salychev +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +#include +#include +#include +#include + +/** + * @brief QBMan software portal interface. + * + * Software portals are used by data path software executing on a processor core + * to communicate with the Queue Manager (QMan) which acts as a central resource + * in DPAA2, managing the queueing of data between multiple processor cores, + * network interfaces, and hardware accelerators in a multicore SoC. + */ +INTERFACE dpaa2_swp; + +/** + * @brief Enqueue multiple frames to a frame queue using one Frame Queue ID. + * + * dev: DPIO device. + * fqid: Frame Queue ID. + * fd: Frame descriptor to enqueue. + * frames_n: Number of frames to enqueue. + */ +METHOD int enq_multiple_fq { + device_t dev; + uint32_t fqid; + struct dpaa2_fd *fd; + int frames_n; +} + +/** + * @brief Configure the channel data availability notification (CDAN) + * in a particular WQ channel paired with DPIO. + * + * dev: DPIO device. + * ctx: Context to configure data availability notifications (CDAN). + */ +METHOD int conf_wq_channel { + device_t dev; + struct dpaa2_io_notif_ctx *ctx; +}; + +/** + * @brief Release one or more buffer pointers to a QBMan buffer pool. + * + * dev: DPIO device. + * bpid: Buffer pool ID. + * buf: Array of buffers physical addresses. + * buf_num: Number of the buffers in the array. + */ +METHOD int release_bufs { + device_t dev; + uint16_t bpid; + bus_addr_t *buf; + uint32_t buf_num; +}; + +/** + * @brief Query current configuration/state of the buffer pool. + * + * dev: DPIO device. + * bpid: Buffer pool ID. + * conf: Configuration/state of the buffer pool. + */ +METHOD int query_bp { + device_t dev; + uint16_t bpid; + struct dpaa2_bp_conf *conf; +} diff --git a/sys/dev/dpaa2/dpaa2_types.h b/sys/dev/dpaa2/dpaa2_types.h new file mode 100644 index 000000000000..b54afa358a28 --- /dev/null +++ b/sys/dev/dpaa2/dpaa2_types.h @@ -0,0 +1,114 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright © 2021-2022 Dmitry Salychev + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _DPAA2_TYPES_H +#define _DPAA2_TYPES_H + +#include + +/** + * @brief Types of the DPAA2 devices. + */ +enum dpaa2_dev_type { + DPAA2_DEV_MC = 7500, /* Management Complex (firmware bus) */ + DPAA2_DEV_RC, /* Resource Container (firmware bus) */ + DPAA2_DEV_IO, /* I/O object (to work with QBMan portal) */ + DPAA2_DEV_NI, /* Network Interface */ + DPAA2_DEV_MCP, /* MC portal */ + DPAA2_DEV_BP, /* Buffer Pool */ + DPAA2_DEV_CON, /* Concentrator */ + DPAA2_DEV_MAC, /* MAC object */ + DPAA2_DEV_MUX, /* MUX (Datacenter bridge) object */ + DPAA2_DEV_SW, /* Ethernet Switch */ + + DPAA2_DEV_NOTYPE /* Shouldn't be assigned to any DPAA2 device. */ +}; + +/** + * @brief Types of the DPAA2 buffers. + */ +enum dpaa2_buf_type { + DPAA2_BUF_RX = 75, /* Rx buffer */ + DPAA2_BUF_TX, /* Tx buffer */ + DPAA2_BUF_STORE /* Channel storage, key configuration */ +}; + +/** + * @brief DMA-mapped buffer (for Rx/Tx buffers, channel storage, etc.). + */ +struct dpaa2_buf { + enum dpaa2_buf_type type; + union { + struct { + bus_dma_tag_t dmat; /* DMA tag for this buffer */ + bus_dmamap_t dmap; + bus_addr_t paddr; + void *vaddr; + + struct mbuf *m; /* associated mbuf */ + } rx; + struct { + bus_dma_tag_t dmat; /* DMA tag for this buffer */ + bus_dmamap_t dmap; + bus_addr_t paddr; + void *vaddr; + + struct mbuf *m; /* associated mbuf */ + uint64_t idx; + + /* for scatter/gather table */ + bus_dma_tag_t sgt_dmat; + bus_dmamap_t sgt_dmap; + bus_addr_t sgt_paddr; + void *sgt_vaddr; + } tx; + struct { + bus_dma_tag_t dmat; /* DMA tag for this buffer */ + bus_dmamap_t dmap; + bus_addr_t paddr; + void *vaddr; + } store; + }; +}; + +struct dpaa2_atomic { + volatile int counter; +}; + +/* Handy wrappers over atomic operations. */ +#define DPAA2_ATOMIC_XCHG(a, val) \ + (atomic_swap_int(&(a)->counter, (val))) +#define DPAA2_ATOMIC_READ(a) \ + (atomic_load_acq_int(&(a)->counter)) +#define DPAA2_ATOMIC_ADD(a, val) \ + (atomic_add_acq_int(&(a)->counter, (val))) + +/* Convert DPAA2 type to/from string. */ +const char *dpaa2_ttos(enum dpaa2_dev_type type); +enum dpaa2_dev_type dpaa2_stot(const char *str); + +#endif /* _DPAA2_TYPES_H */ diff --git a/sys/dev/dpaa2/memac_mdio.h b/sys/dev/dpaa2/memac_mdio.h new file mode 100644 index 000000000000..02e4c081f87c --- /dev/null +++ b/sys/dev/dpaa2/memac_mdio.h @@ -0,0 +1,64 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright © 2021-2022 Bjoern A. Zeeb + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef __MEMAC_MDIO_H +#define __MEMAC_MDIO_H + +/* -------------------------------------------------------------------------- */ + +struct memacphy_softc_common { + device_t dev; + device_t dpnidev; + int phy; +}; + +int memacphy_miibus_readreg(device_t, int, int); +int memacphy_miibus_writereg(device_t, int, int, int); +void memacphy_miibus_statchg(struct memacphy_softc_common *); +int memacphy_set_ni_dev(struct memacphy_softc_common *, device_t); +int memacphy_get_phy_loc(struct memacphy_softc_common *, int *); + + +/* -------------------------------------------------------------------------- */ + +struct memac_mdio_softc_common { + device_t dev; + struct resource *mem_res; + bool is_little_endian; +}; + +int memac_miibus_readreg(struct memac_mdio_softc_common *, int, int); +int memac_miibus_writereg(struct memac_mdio_softc_common *, int, int, int); + +ssize_t memac_mdio_get_property(device_t, device_t, const char *, + void *, size_t, device_property_type_t); +int memac_mdio_read_ivar(device_t, device_t, int, uintptr_t *); + +int memac_mdio_generic_attach(struct memac_mdio_softc_common *); +int memac_mdio_generic_detach(struct memac_mdio_softc_common *); + +#endif /* __MEMAC_MDIO_H */ diff --git a/sys/dev/dpaa2/memac_mdio_acpi.c b/sys/dev/dpaa2/memac_mdio_acpi.c new file mode 100644 index 000000000000..ed0c2802f60f --- /dev/null +++ b/sys/dev/dpaa2/memac_mdio_acpi.c @@ -0,0 +1,310 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright © 2021-2022 Bjoern A. Zeeb + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include + +#include "memac_mdio.h" +#include "memac_mdio_if.h" +#include "acpi_bus_if.h" +#include "miibus_if.h" + +/* -------------------------------------------------------------------------- */ + +struct memacphy_softc_acpi { + struct memacphy_softc_common scc; + int uid; + uint64_t phy_channel; + char compatible[64]; +}; + +static void +memacphy_acpi_miibus_statchg(device_t dev) +{ + struct memacphy_softc_acpi *sc; + + sc = device_get_softc(dev); + memacphy_miibus_statchg(&sc->scc); +} + +static int +memacphy_acpi_set_ni_dev(device_t dev, device_t nidev) +{ + struct memacphy_softc_acpi *sc; + + sc = device_get_softc(dev); + return (memacphy_set_ni_dev(&sc->scc, nidev)); +} + +static int +memacphy_acpi_get_phy_loc(device_t dev, int *phy_loc) +{ + struct memacphy_softc_acpi *sc; + + sc = device_get_softc(dev); + return (memacphy_get_phy_loc(&sc->scc, phy_loc)); +} + +static int +memacphy_acpi_probe(device_t dev) +{ + + device_set_desc(dev, "MEMAC PHY (acpi)"); + return (BUS_PROBE_DEFAULT); +} + +static int +memacphy_acpi_attach(device_t dev) +{ + struct memacphy_softc_acpi *sc; + ACPI_HANDLE h; + ssize_t s; + + sc = device_get_softc(dev); + sc->scc.dev = dev; + h = acpi_get_handle(dev); + + s = acpi_GetInteger(h, "_UID", &sc->uid); + if (ACPI_FAILURE(s)) { + device_printf(dev, "Cannot get '_UID' property: %zd\n", s); + return (ENXIO); + } + + s = device_get_property(dev, "phy-channel", + &sc->phy_channel, sizeof(sc->phy_channel), DEVICE_PROP_UINT64); + if (s != -1) + sc->scc.phy = sc->phy_channel; + else + sc->scc.phy = -1; + s = device_get_property(dev, "compatible", + sc->compatible, sizeof(sc->compatible), DEVICE_PROP_ANY); + + if (bootverbose) + device_printf(dev, "UID %#04x phy-channel %ju compatible '%s' phy %u\n", + sc->uid, sc->phy_channel, + sc->compatible[0] != '\0' ? sc->compatible : "", sc->scc.phy); + + if (sc->scc.phy == -1) + return (ENXIO); + return (0); +} + +static device_method_t memacphy_acpi_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, memacphy_acpi_probe), + DEVMETHOD(device_attach, memacphy_acpi_attach), + DEVMETHOD(device_detach, bus_generic_detach), + + /* MII interface */ + DEVMETHOD(miibus_readreg, memacphy_miibus_readreg), + DEVMETHOD(miibus_writereg, memacphy_miibus_writereg), + DEVMETHOD(miibus_statchg, memacphy_acpi_miibus_statchg), + + /* memac */ + DEVMETHOD(memac_mdio_set_ni_dev, memacphy_acpi_set_ni_dev), + DEVMETHOD(memac_mdio_get_phy_loc, memacphy_acpi_get_phy_loc), + + DEVMETHOD_END +}; + +DEFINE_CLASS_0(memacphy_acpi, memacphy_acpi_driver, memacphy_acpi_methods, + sizeof(struct memacphy_softc_acpi)); + +EARLY_DRIVER_MODULE(memacphy_acpi, memac_mdio_acpi, memacphy_acpi_driver, 0, 0, + BUS_PASS_SUPPORTDEV); +DRIVER_MODULE(miibus, memacphy_acpi, miibus_driver, 0, 0); +MODULE_DEPEND(memacphy_acpi, miibus, 1, 1, 1); + +/* -------------------------------------------------------------------------- */ + +struct memac_mdio_softc_acpi { + struct memac_mdio_softc_common scc; +}; + +static int +memac_acpi_miibus_readreg(device_t dev, int phy, int reg) +{ + struct memac_mdio_softc_acpi *sc; + + sc = device_get_softc(dev); + return (memac_miibus_readreg(&sc->scc, phy, reg)); +} + +static int +memac_acpi_miibus_writereg(device_t dev, int phy, int reg, int data) +{ + struct memac_mdio_softc_acpi *sc; + + sc = device_get_softc(dev); + return (memac_miibus_writereg(&sc->scc, phy, reg, data)); +} + +/* Context for walking PHY child devices. */ +struct memac_mdio_walk_ctx { + device_t dev; + int count; + int countok; +}; + +static char *memac_mdio_ids[] = { + "NXP0006", + NULL +}; + +static int +memac_mdio_acpi_probe(device_t dev) +{ + int rc; + + if (acpi_disabled("fsl_memac_mdio")) + return (ENXIO); + + rc = ACPI_ID_PROBE(device_get_parent(dev), dev, memac_mdio_ids, NULL); + if (rc <= 0) + device_set_desc(dev, "Freescale XGMAC MDIO Bus"); + + return (rc); +} + +static ACPI_STATUS +memac_mdio_acpi_probe_child(ACPI_HANDLE h, device_t *dev, int level, void *arg) +{ + struct memac_mdio_walk_ctx *ctx; + struct acpi_device *ad; + device_t child; + uint32_t adr; + + ctx = (struct memac_mdio_walk_ctx *)arg; + ctx->count++; + + if (ACPI_FAILURE(acpi_GetInteger(h, "_ADR", &adr))) + return (AE_OK); + + /* Technically M_ACPIDEV */ + if ((ad = malloc(sizeof(*ad), M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) + return (AE_OK); + + child = device_add_child(ctx->dev, "memacphy_acpi", -1); + if (child == NULL) { + free(ad, M_DEVBUF); + return (AE_OK); + } + ad->ad_handle = h; + ad->ad_cls_class = 0xffffff; + resource_list_init(&ad->ad_rl); + device_set_ivars(child, ad); + *dev = child; + + ctx->countok++; + return (AE_OK); +} + +static int +memac_mdio_acpi_attach(device_t dev) +{ + struct memac_mdio_softc_acpi *sc; + struct memac_mdio_walk_ctx ctx; + int error; + + sc = device_get_softc(dev); + sc->scc.dev = dev; + + error = memac_mdio_generic_attach(&sc->scc); + if (error != 0) + return (error); + + ctx.dev = dev; + ctx.count = 0; + ctx.countok = 0; + ACPI_SCAN_CHILDREN(device_get_parent(dev), dev, 1, + memac_mdio_acpi_probe_child, &ctx); + if (ctx.countok > 0) { + bus_generic_probe(dev); + bus_generic_attach(dev); + } + + return (0); +} + +static int +memac_mdio_acpi_detach(device_t dev) +{ + struct memac_mdio_softc_acpi *sc; + + sc = device_get_softc(dev); + return (memac_mdio_generic_detach(&sc->scc)); +} + +static device_method_t memac_mdio_acpi_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, memac_mdio_acpi_probe), + DEVMETHOD(device_attach, memac_mdio_acpi_attach), + DEVMETHOD(device_detach, memac_mdio_acpi_detach), + + /* MII interface */ + DEVMETHOD(miibus_readreg, memac_acpi_miibus_readreg), + DEVMETHOD(miibus_writereg, memac_acpi_miibus_writereg), + + /* .. */ + DEVMETHOD(bus_add_child, bus_generic_add_child), + DEVMETHOD(bus_read_ivar, memac_mdio_read_ivar), + DEVMETHOD(bus_get_property, memac_mdio_get_property), + + DEVMETHOD_END +}; + +DEFINE_CLASS_0(memac_mdio_acpi, memac_mdio_acpi_driver, memac_mdio_acpi_methods, + sizeof(struct memac_mdio_softc_acpi)); + +EARLY_DRIVER_MODULE(memac_mdio_acpi, acpi, memac_mdio_acpi_driver, 0, 0, + BUS_PASS_SUPPORTDEV); + +DRIVER_MODULE(miibus, memac_mdio_acpi, miibus_driver, 0, 0); +MODULE_DEPEND(memac_mdio_acpi, miibus, 1, 1, 1); +MODULE_VERSION(memac_mdio_acpi, 1); diff --git a/sys/dev/dpaa2/memac_mdio_common.c b/sys/dev/dpaa2/memac_mdio_common.c new file mode 100644 index 000000000000..a004966ed89a --- /dev/null +++ b/sys/dev/dpaa2/memac_mdio_common.c @@ -0,0 +1,306 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright © 2021-2022 Bjoern A. Zeeb + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include + +#include "memac_mdio.h" +#include "miibus_if.h" + +/* #define MEMAC_MDIO_DEBUG */ + +/* -------------------------------------------------------------------------- */ + +int +memacphy_miibus_readreg(device_t dev, int phy, int reg) +{ + + return (MIIBUS_READREG(device_get_parent(dev), phy, reg)); +} + +int +memacphy_miibus_writereg(device_t dev, int phy, int reg, int data) +{ + + return (MIIBUS_WRITEREG(device_get_parent(dev), phy, reg, data)); +} + +void +memacphy_miibus_statchg(struct memacphy_softc_common *sc) +{ + + if (sc->dpnidev != NULL) + MIIBUS_STATCHG(sc->dpnidev); +} + +int +memacphy_set_ni_dev(struct memacphy_softc_common *sc, device_t nidev) +{ + + if (nidev == NULL) + return (EINVAL); + +#if defined(MEMAC_MDIO_DEBUG) + if (bootverbose) + device_printf(sc->dev, "setting nidev %p (%s)\n", + nidev, device_get_nameunit(nidev)); +#endif + + if (sc->dpnidev != NULL) + return (EBUSY); + + sc->dpnidev = nidev; + return (0); +} + +int +memacphy_get_phy_loc(struct memacphy_softc_common *sc, int *phy_loc) +{ + int error; + + if (phy_loc == NULL) + return (EINVAL); + + if (sc->phy == -1) { + *phy_loc = MII_PHY_ANY; + error = ENODEV; + } else { + *phy_loc = sc->phy; + error = 0; + } + +#if defined(MEMAC_MDIO_DEBUG) + if (bootverbose) + device_printf(sc->dev, "returning phy_loc %d, error %d\n", + *phy_loc, error); +#endif + + return (error); +} + +/* -------------------------------------------------------------------------- */ + +/* + * MDIO Ethernet Management Interface Registers (internal PCS MDIO PHY) + * 0x0030 MDIO Configuration Register (MDIO_CFG) + * 0x0034 MDIO Control Register (MDIO_CTL) + * 0x0038 MDIO Data Register (MDIO_DATA) + * 0x003c MDIO Register Address Register (MDIO_ADDR) + * + * External MDIO interfaces + * 0x0030 External MDIO Configuration Register (EMDIO_CFG) + * 0x0034 External MDIO Control Register (EMDIO_CTL) + * 0x0038 External MDIO Data Register (EMDIO_DATA) + * 0x003c External MDIO Register Address Register (EMDIO_ADDR) + */ +#define MDIO_CFG 0x00030 +#define MDIO_CFG_MDIO_RD_ER (1 << 1) +#define MDIO_CFG_ENC45 (1 << 6) +#define MDIO_CFG_BUSY (1 << 31) +#define MDIO_CTL 0x00034 +#define MDIO_CTL_READ (1 << 15) +#define MDIO_CTL_PORT_ADDR(_x) (((_x) & 0x1f) << 5) +#define MDIO_CTL_DEV_ADDR(_x) ((_x) & 0x1f) +#define MDIO_DATA 0x00038 +#define MDIO_ADDR 0x0003c + +static uint32_t +memac_read_4(struct memac_mdio_softc_common *sc, uint32_t reg) +{ + uint32_t v, r; + + v = bus_read_4(sc->mem_res, reg); + if (sc->is_little_endian) + r = le32toh(v); + else + r = be32toh(v); + + return (r); +} + +static void +memac_write_4(struct memac_mdio_softc_common *sc, uint32_t reg, uint32_t val) +{ + uint32_t v; + + if (sc->is_little_endian) + v = htole32(val); + else + v = htobe32(val); + bus_write_4(sc->mem_res, reg, v); +} + +static uint32_t +memac_miibus_wait_no_busy(struct memac_mdio_softc_common *sc) +{ + uint32_t count, val; + + for (count = 1000; count > 0; count--) { + val = memac_read_4(sc, MDIO_CFG); + if ((val & MDIO_CFG_BUSY) == 0) + break; + DELAY(1); + } + + if (count == 0) + return (0xffff); + + return (0); +} + +int +memac_miibus_readreg(struct memac_mdio_softc_common *sc, int phy, int reg) +{ + uint32_t cfg, ctl, val; + + /* Set proper Clause 45 mode. */ + cfg = memac_read_4(sc, MDIO_CFG); + /* XXX 45 support? */ + cfg &= ~MDIO_CFG_ENC45; /* Use Clause 22 */ + memac_write_4(sc, MDIO_CFG, cfg); + + val = memac_miibus_wait_no_busy(sc); + if (val != 0) + return (0xffff); + + /* To whom do we want to talk to.. */ + ctl = MDIO_CTL_PORT_ADDR(phy) | MDIO_CTL_DEV_ADDR(reg); + /* XXX do we need two writes for this to work reliably? */ + memac_write_4(sc, MDIO_CTL, ctl | MDIO_CTL_READ); + + val = memac_miibus_wait_no_busy(sc); + if (val != 0) + return (0xffff); + + cfg = memac_read_4(sc, MDIO_CFG); + if (cfg & MDIO_CFG_MDIO_RD_ER) + return (0xffff); + + val = memac_read_4(sc, MDIO_DATA); + val &= 0xffff; + +#if defined(MEMAC_MDIO_DEBUG) + device_printf(sc->dev, "phy read %d:%d = %#06x\n", phy, reg, val); +#endif + + return (val); +} + +int +memac_miibus_writereg(struct memac_mdio_softc_common *sc, int phy, int reg, int data) +{ + uint32_t cfg, ctl, val; + +#if defined(MEMAC_MDIO_DEBUG) + device_printf(sc->dev, "phy write %d:%d\n", phy, reg); +#endif + + /* Set proper Clause 45 mode. */ + cfg = memac_read_4(sc, MDIO_CFG); + /* XXX 45 support? */ + cfg &= ~MDIO_CFG_ENC45; /* Use Clause 22 */ + memac_write_4(sc, MDIO_CFG, cfg); + + val = memac_miibus_wait_no_busy(sc); + if (val != 0) + return (0xffff); + + /* To whom do we want to talk to.. */ + ctl = MDIO_CTL_PORT_ADDR(phy) | MDIO_CTL_DEV_ADDR(reg); + memac_write_4(sc, MDIO_CTL, ctl); + + memac_write_4(sc, MDIO_DATA, data & 0xffff); + + val = memac_miibus_wait_no_busy(sc); + if (val != 0) + return (0xffff); + + return (0); +} + +ssize_t +memac_mdio_get_property(device_t dev, device_t child, const char *propname, + void *propvalue, size_t size, device_property_type_t type) +{ + + return (bus_generic_get_property(dev, child, propname, propvalue, size, type)); +} + +int +memac_mdio_read_ivar(device_t dev, device_t child, int index, uintptr_t *result) +{ + + return (BUS_READ_IVAR(device_get_parent(dev), dev, index, result)); +} + + +int +memac_mdio_generic_attach(struct memac_mdio_softc_common *sc) +{ + int rid; + + rid = 0; + sc->mem_res = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY, + &rid, RF_ACTIVE | RF_SHAREABLE); + if (sc->mem_res == NULL) { + device_printf(sc->dev, "%s: cannot allocate mem resource\n", + __func__); + return (ENXIO); + } + + sc->is_little_endian = device_has_property(sc->dev, "little-endian"); + + return (0); +} + +int +memac_mdio_generic_detach(struct memac_mdio_softc_common *sc) +{ + + if (sc->mem_res != NULL) + bus_release_resource(sc->dev, SYS_RES_MEMORY, + rman_get_rid(sc->mem_res), sc->mem_res); + + return (0); +} diff --git a/sys/dev/dpaa2/memac_mdio_fdt.c b/sys/dev/dpaa2/memac_mdio_fdt.c new file mode 100644 index 000000000000..8e349566b9e5 --- /dev/null +++ b/sys/dev/dpaa2/memac_mdio_fdt.c @@ -0,0 +1,308 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright © 2021-2022 Bjoern A. Zeeb + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "memac_mdio.h" +#include "memac_mdio_if.h" +#include "ofw_bus_if.h" +#include "miibus_if.h" + +/* -------------------------------------------------------------------------- */ + +struct memacphy_softc_fdt { + struct memacphy_softc_common scc; + uint32_t reg; + phandle_t xref; +}; + +static void +memacphy_fdt_miibus_statchg(device_t dev) +{ + struct memacphy_softc_fdt *sc; + + sc = device_get_softc(dev); + memacphy_miibus_statchg(&sc->scc); +} + +static int +memacphy_fdt_set_ni_dev(device_t dev, device_t nidev) +{ + struct memacphy_softc_fdt *sc; + + sc = device_get_softc(dev); + return (memacphy_set_ni_dev(&sc->scc, nidev)); +} + +static int +memacphy_fdt_get_phy_loc(device_t dev, int *phy_loc) +{ + struct memacphy_softc_fdt *sc; + + sc = device_get_softc(dev); + return (memacphy_get_phy_loc(&sc->scc, phy_loc)); +} + +static int +memacphy_fdt_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + device_set_desc(dev, "MEMAC PHY (fdt)"); + return (BUS_PROBE_DEFAULT); +} + +static int +memacphy_fdt_attach(device_t dev) +{ + struct memacphy_softc_fdt *sc; + phandle_t node; + ssize_t s; + int error; + + sc = device_get_softc(dev); + sc->scc.dev = dev; + node = ofw_bus_get_node(dev); + + s = device_get_property(dev, "reg", &sc->reg, sizeof(sc->reg), + DEVICE_PROP_UINT32); + if (s != -1) + sc->scc.phy = sc->reg; + else + sc->scc.phy = -1; + sc->xref = OF_xref_from_node(node); + + error = OF_device_register_xref(sc->xref, dev); + if (error != 0) + device_printf(dev, "Failed to register xref %#x\n", sc->xref); + + if (bootverbose) + device_printf(dev, "node %#x '%s': reg %#x xref %#x phy %u\n", + node, ofw_bus_get_name(dev), sc->reg, sc->xref, sc->scc.phy); + + if (sc->scc.phy == -1) + error = ENXIO; + return (error); +} + +static device_method_t memacphy_fdt_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, memacphy_fdt_probe), + DEVMETHOD(device_attach, memacphy_fdt_attach), + DEVMETHOD(device_detach, bus_generic_detach), + + /* MII interface */ + DEVMETHOD(miibus_readreg, memacphy_miibus_readreg), + DEVMETHOD(miibus_writereg, memacphy_miibus_writereg), + DEVMETHOD(miibus_statchg, memacphy_fdt_miibus_statchg), + + /* memac */ + DEVMETHOD(memac_mdio_set_ni_dev, memacphy_fdt_set_ni_dev), + DEVMETHOD(memac_mdio_get_phy_loc, memacphy_fdt_get_phy_loc), + + DEVMETHOD_END +}; + +DEFINE_CLASS_0(memacphy_fdt, memacphy_fdt_driver, memacphy_fdt_methods, + sizeof(struct memacphy_softc_fdt)); + +EARLY_DRIVER_MODULE(memacphy_fdt, memac_mdio_fdt, memacphy_fdt_driver, 0, 0, + BUS_PASS_SUPPORTDEV); +DRIVER_MODULE(miibus, memacphy_fdt, miibus_driver, 0, 0); +MODULE_DEPEND(memacphy_fdt, miibus, 1, 1, 1); + +/* -------------------------------------------------------------------------- */ + +/* + * Order in this softc is important; memac_mdio_fdt_attach() calls + * simplebus_init() which expects sb_sc at the beginning. + */ +struct memac_mdio_softc_fdt { + struct simplebus_softc sb_sc; /* Must stay first. */ + struct memac_mdio_softc_common scc; +}; + +static int +memac_fdt_miibus_readreg(device_t dev, int phy, int reg) +{ + struct memac_mdio_softc_fdt *sc; + + sc = device_get_softc(dev); + return (memac_miibus_readreg(&sc->scc, phy, reg)); +} + +static int +memac_fdt_miibus_writereg(device_t dev, int phy, int reg, int data) +{ + struct memac_mdio_softc_fdt *sc; + + sc = device_get_softc(dev); + return (memac_miibus_writereg(&sc->scc, phy, reg, data)); +} + +static struct ofw_compat_data compat_data[] = { + { "fsl,fman-memac-mdio", 1 }, + { NULL, 0 } +}; + +static int +memac_mdio_fdt_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) + return (ENXIO); + + device_set_desc(dev, "Freescale XGMAC MDIO Bus (FDT)"); + return (BUS_PROBE_DEFAULT); +} + +static int +memac_mdio_fdt_probe_child(device_t bus, phandle_t child) +{ + device_t childdev; + + /* Make sure we do not aliready have a device. */ + childdev = ofw_bus_find_child_device_by_phandle(bus, child); + if (childdev != NULL) + return (0); + + childdev = simplebus_add_device(bus, child, 0, NULL, -1, NULL); + if (childdev == NULL) + return (ENXIO); + + return (device_probe_and_attach(childdev)); +} + +static int +memac_mdio_fdt_attach(device_t dev) +{ + struct memac_mdio_softc_fdt *sc; + phandle_t node, child; + int error; + + sc = device_get_softc(dev); + sc->scc.dev = dev; + + error = memac_mdio_generic_attach(&sc->scc); + if (error != 0) + return (error); + + /* Attach the *phy* children represented in the device tree. */ + bus_generic_probe(dev); + bus_enumerate_hinted_children(dev); + node = ofw_bus_get_node(dev); + simplebus_init(dev, node); + for (child = OF_child(node); child > 0; child = OF_peer(child)) { + if (!OF_hasprop(child, "reg")) + continue; + if (memac_mdio_fdt_probe_child(dev, child) != 0) + continue; + } + + return (0); +} + +static int +memac_mdio_fdt_detach(device_t dev) +{ + struct memac_mdio_softc_fdt *sc; + + sc = device_get_softc(dev); + return (memac_mdio_generic_detach(&sc->scc)); +} + +static const struct ofw_bus_devinfo * +memac_simplebus_get_devinfo(device_t bus, device_t child) +{ + + return (OFW_BUS_GET_DEVINFO(device_get_parent(bus), child)); +} + +static device_method_t memac_mdio_fdt_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, memac_mdio_fdt_probe), + DEVMETHOD(device_attach, memac_mdio_fdt_attach), + DEVMETHOD(device_detach, memac_mdio_fdt_detach), + + /* MII interface */ + DEVMETHOD(miibus_readreg, memac_fdt_miibus_readreg), + DEVMETHOD(miibus_writereg, memac_fdt_miibus_writereg), + + /* OFW/simplebus */ + DEVMETHOD(ofw_bus_get_devinfo, memac_simplebus_get_devinfo), + DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), + DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), + DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), + DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), + DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), + + /* Bus interface */ + DEVMETHOD(bus_add_child, bus_generic_add_child), + DEVMETHOD(bus_read_ivar, memac_mdio_read_ivar), + DEVMETHOD(bus_get_property, memac_mdio_get_property), + + DEVMETHOD_END +}; + +DEFINE_CLASS_0(memac_mdio_fdt, memac_mdio_fdt_driver, memac_mdio_fdt_methods, + sizeof(struct memac_mdio_softc_fdt)); + +EARLY_DRIVER_MODULE(memac_mdio_fdt, simplebus, memac_mdio_fdt_driver, 0, 0, + BUS_PASS_SUPPORTDEV); + +DRIVER_MODULE(miibus, memac_mdio_fdt, miibus_driver, 0, 0); +MODULE_DEPEND(memac_mdio_fdt, miibus, 1, 1, 1); +MODULE_VERSION(memac_mdio_fdt, 1); diff --git a/sys/dev/dpaa2/memac_mdio_if.m b/sys/dev/dpaa2/memac_mdio_if.m new file mode 100644 index 000000000000..d49c95641409 --- /dev/null +++ b/sys/dev/dpaa2/memac_mdio_if.m @@ -0,0 +1,42 @@ +#- +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright © 2022, Bjoern A. Zeeb +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# + +#include + +INTERFACE memac_mdio; + +METHOD int set_ni_dev { + device_t dev; + device_t nidev; +}; + +METHOD int get_phy_loc { + device_t dev; + int *phy_loc; +}; diff --git a/sys/modules/Makefile b/sys/modules/Makefile index 53936d77d6ff..f381675a9b9f 100644 --- a/sys/modules/Makefile +++ b/sys/modules/Makefile @@ -1,906 +1,908 @@ # $FreeBSD$ SYSDIR?=${SRCTOP}/sys .include "${SYSDIR}/conf/kern.opts.mk" SUBDIR_PARALLEL= # Modules that include binary-only blobs of microcode should be selectable by # MK_SOURCELESS_UCODE option (see below). .include "${SYSDIR}/conf/config.mk" .if defined(MODULES_OVERRIDE) && !defined(ALL_MODULES) SUBDIR=${MODULES_OVERRIDE} .else SUBDIR= \ ${_3dfx} \ ${_3dfx_linux} \ ${_aac} \ ${_aacraid} \ accf_data \ accf_dns \ accf_http \ acl_nfs4 \ acl_posix1e \ ${_acpi} \ ae \ ${_aesni} \ age \ ${_agp} \ ahci \ aic7xxx \ alc \ ale \ alq \ ${_amd_ecc_inject} \ ${_amdgpio} \ ${_amdsbwd} \ ${_amdsmn} \ ${_amdtemp} \ amr \ ${_an} \ ${_aout} \ ${_arcmsr} \ ${_allwinner} \ ${_armv8crypto} \ ${_asmc} \ ata \ ath \ ath_dfs \ ath_hal \ ath_hal_ar5210 \ ath_hal_ar5211 \ ath_hal_ar5212 \ ath_hal_ar5416 \ ath_hal_ar9300 \ ath_main \ ath_rate \ ath_pci \ ${_autofs} \ axgbe \ backlight \ ${_bce} \ ${_bcm283x_clkman} \ ${_bcm283x_pwm} \ bfe \ bge \ bhnd \ ${_bxe} \ ${_bios} \ ${_blake2} \ ${_bnxt} \ bridgestp \ bwi \ bwn \ ${_bytgpio} \ ${_chvgpio} \ cam \ ${_cardbus} \ ${_carp} \ cas \ ${_cbb} \ cc \ ${_ccp} \ cd9660 \ cd9660_iconv \ ${_ce} \ ${_cfi} \ ${_chromebook_platform} \ ${_ciss} \ cloudabi \ ${_cloudabi32} \ ${_cloudabi64} \ ${_coretemp} \ ${_cp} \ ${_cpsw} \ ${_cpuctl} \ ${_cpufreq} \ ${_crypto} \ ${_cryptodev} \ ctl \ ${_cxgb} \ ${_cxgbe} \ dc \ dcons \ dcons_crom \ + ${_dpaa2} \ ${_dpdk_lpm4} \ ${_dpdk_lpm6} \ ${_dpms} \ dummynet \ ${_dwwdt} \ ${_efirt} \ ${_em} \ ${_ena} \ esp \ ${_et} \ evdev \ ${_exca} \ ext2fs \ fdc \ fdescfs \ ${_ffec} \ ${_fib_dxr} \ filemon \ firewire \ firmware \ ${_ftwd} \ fusefs \ ${_fxp} \ gem \ geom \ ${_glxiic} \ ${_glxsb} \ gpio \ hid \ hifn \ ${_hpt27xx} \ ${_hptiop} \ ${_hptmv} \ ${_hptnr} \ ${_hptrr} \ hwpmc \ ${_hwpmc_mips24k} \ ${_hwpmc_mips74k} \ ${_hyperv} \ i2c \ ${_iavf} \ ${_ibcore} \ ${_ichwd} \ ${_ice} \ ${_ice_ddp} \ ${_ida} \ if_bridge \ ${_if_cgem} \ if_disc \ if_edsc \ ${_if_enc} \ if_epair \ ${_if_gif} \ ${_if_gre} \ ${_if_me} \ if_infiniband \ if_lagg \ ${_if_ndis} \ ${_if_stf} \ if_tuntap \ if_vlan \ if_vxlan \ ${_if_wg} \ iflib \ ${_igc} \ ${_iir} \ imgact_binmisc \ ${_intelspi} \ ${_io} \ ${_ioat} \ ${_ipoib} \ ${_ipdivert} \ ${_ipfilter} \ ${_ipfw} \ ipfw_nat \ ${_ipfw_nat64} \ ${_ipfw_nptv6} \ ${_ipfw_pmod} \ ${_ipmi} \ ip6_mroute_mod \ ip_mroute_mod \ ${_ips} \ ${_ipsec} \ ${_ipw} \ ${_ipwfw} \ ${_isci} \ ${_iser} \ isp \ ${_ispfw} \ ${_itwd} \ ${_iwi} \ ${_iwifw} \ ${_iwlwifi} \ ${_iwlwififw} \ ${_iwm} \ ${_iwmfw} \ ${_iwn} \ ${_iwnfw} \ ${_ix} \ ${_ixv} \ ${_ixl} \ jme \ kbdmux \ kgssapi \ kgssapi_krb5 \ khelp \ krpc \ ksyms \ ${_ktls_ocf} \ le \ lge \ libalias \ libiconv \ libmchain \ lindebugfs \ linuxkpi \ linuxkpi_wlan \ ${_lio} \ lpt \ ${_mac_biba} \ ${_mac_bsdextended} \ ${_mac_ifoff} \ ${_mac_lomac} \ ${_mac_mls} \ ${_mac_none} \ ${_mac_ntpd} \ ${_mac_partition} \ ${_mac_portacl} \ ${_mac_priority} \ ${_mac_seeotheruids} \ ${_mac_stub} \ ${_mac_test} \ ${_malo} \ md \ mdio \ ${_mgb} \ mem \ mfi \ mii \ mlx \ mlxfw \ ${_mlx4} \ ${_mlx4ib} \ ${_mlx4en} \ ${_mlx5} \ ${_mlx5en} \ ${_mlx5ib} \ ${_mly} \ mmc \ mmcsd \ ${_mpr} \ ${_mps} \ mpt \ mqueue \ mrsas \ msdosfs \ msdosfs_iconv \ msk \ ${_mthca} \ mvs \ mwl \ ${_mwlfw} \ mxge \ my \ ${_nctgpio} \ ${_ndis} \ ${_netgraph} \ ${_nfe} \ nfscl \ nfscommon \ nfsd \ nfslockd \ nfssvc \ nge \ nmdm \ nullfs \ ${_ntb} \ ${_nvd} \ ${_nvdimm} \ ${_nvme} \ ${_nvram} \ oce \ ${_ocs_fc} \ ${_ossl} \ otus \ ${_otusfw} \ ow \ ${_padlock} \ ${_padlock_rng} \ ${_pccard} \ ${_pchtherm} \ ${_pcfclock} \ ${_pf} \ ${_pflog} \ ${_pfsync} \ plip \ ${_pms} \ ppbus \ ppc \ ppi \ pps \ procfs \ proto \ pseudofs \ ${_pst} \ pty \ puc \ pwm \ ${_qat} \ ${_qatfw} \ ${_qlxge} \ ${_qlxgb} \ ${_qlxgbe} \ ${_qlnx} \ ral \ ${_ralfw} \ ${_random_fortuna} \ ${_random_other} \ rc4 \ ${_rdma} \ ${_rdrand_rng} \ re \ rl \ ${_rockchip} \ rtsx \ ${_rtw88} \ ${_rtw88fw} \ rtwn \ rtwn_pci \ rtwn_usb \ ${_rtwnfw} \ ${_s3} \ ${_safe} \ safexcel \ ${_sbni} \ scc \ ${_sctp} \ sdhci \ ${_sdhci_acpi} \ ${_sdhci_fdt} \ sdhci_pci \ sdio \ sem \ send \ ${_sfxge} \ sge \ ${_sgx} \ ${_sgx_linux} \ siftr \ siis \ sis \ sk \ ${_smartpqi} \ smbfs \ snp \ sound \ ${_speaker} \ spi \ ${_splash} \ ${_sppp} \ ste \ stge \ ${_sume} \ ${_superio} \ ${_sym} \ ${_syscons} \ sysvipc \ tcp \ ${_ti} \ tmpfs \ ${_toecore} \ ${_tpm} \ ${_twa} \ twe \ tws \ uart \ udf \ udf_iconv \ ufs \ uinput \ unionfs \ usb \ ${_vesa} \ virtio \ vge \ ${_viawd} \ videomode \ vkbd \ ${_vmd} \ ${_vmm} \ ${_vmware} \ vr \ vte \ ${_wbwd} \ wlan \ wlan_acl \ wlan_amrr \ wlan_ccmp \ wlan_rssadapt \ wlan_tkip \ wlan_wep \ wlan_xauth \ ${_wpi} \ ${_wpifw} \ ${_x86bios} \ xdr \ xl \ xz \ zlib .if ${MK_AUTOFS} != "no" || defined(ALL_MODULES) _autofs= autofs .endif .if ${MK_CDDL} != "no" || defined(ALL_MODULES) .if (${MACHINE_CPUARCH} != "arm" || ${MACHINE_ARCH:Marmv[67]*} != "") && \ ${MACHINE_CPUARCH} != "mips" .if ${KERN_OPTS:MKDTRACE_HOOKS} SUBDIR+= dtrace .endif .endif SUBDIR+= opensolaris .endif # Requires bus_space_read_8 .if ${MACHINE_ARCH} != "i386" _bnxt= bnxt .endif .if ${MK_CRYPT} != "no" || defined(ALL_MODULES) .if exists(${SRCTOP}/sys/opencrypto) _crypto= crypto _cryptodev= cryptodev _random_fortuna=random_fortuna _random_other= random_other _ktls_ocf= ktls_ocf .endif .endif .if ${MK_CUSE} != "no" || defined(ALL_MODULES) SUBDIR+= cuse .endif .if ${MK_EFI} != "no" .if ${MACHINE_CPUARCH} == "aarch64" || ${MACHINE_CPUARCH} == "amd64" _efirt= efirt .endif .endif .if (${MK_INET_SUPPORT} != "no" || ${MK_INET6_SUPPORT} != "no") || \ defined(ALL_MODULES) _carp= carp _toecore= toecore _if_enc= if_enc _if_gif= if_gif _if_gre= if_gre .if ${MK_CRYPT} != "no" || defined(ALL_MODULES) _if_wg= if_wg .endif _ipfw_pmod= ipfw_pmod .if ${KERN_OPTS:MIPSEC_SUPPORT} && !${KERN_OPTS:MIPSEC} _ipsec= ipsec .endif .if ${KERN_OPTS:MSCTP_SUPPORT} || ${KERN_OPTS:MSCTP} _sctp= sctp .endif .endif .if (${MK_INET_SUPPORT} != "no" && ${MK_INET6_SUPPORT} != "no") || \ defined(ALL_MODULES) _if_stf= if_stf .endif .if ${MK_INET_SUPPORT} != "no" || defined(ALL_MODULES) _if_me= if_me _ipdivert= ipdivert _ipfw= ipfw .if ${MK_INET6_SUPPORT} != "no" || defined(ALL_MODULES) _ipfw_nat64= ipfw_nat64 .endif .endif .if ${MK_INET6_SUPPORT} != "no" || defined(ALL_MODULES) _ipfw_nptv6= ipfw_nptv6 .endif .if ${MK_IPFILTER} != "no" || defined(ALL_MODULES) _ipfilter= ipfilter .endif .if ${MK_INET_SUPPORT} != "no" && ${KERN_OPTS:MFIB_ALGO} _dpdk_lpm4= dpdk_lpm4 _fib_dxr= fib_dxr .endif .if ${MK_INET6_SUPPORT} != "no" && ${KERN_OPTS:MFIB_ALGO} _dpdk_lpm6= dpdk_lpm6 .endif .if ${MK_ISCSI} != "no" || defined(ALL_MODULES) SUBDIR+= cfiscsi SUBDIR+= iscsi SUBDIR+= iscsi_initiator .endif .if !empty(OPT_FDT) SUBDIR+= fdt .endif # Linuxulator .if ${MACHINE_CPUARCH} == "aarch64" || ${MACHINE_CPUARCH} == "amd64" || \ ${MACHINE_CPUARCH} == "i386" SUBDIR+= linprocfs SUBDIR+= linsysfs .endif .if ${MACHINE_CPUARCH} == "amd64" || ${MACHINE_CPUARCH} == "i386" SUBDIR+= linux .endif .if ${MACHINE_CPUARCH} == "aarch64" || ${MACHINE_CPUARCH} == "amd64" SUBDIR+= linux64 SUBDIR+= linux_common .endif .if ${MACHINE_CPUARCH} == "aarch64" || ${MACHINE_CPUARCH} == "amd64" || \ ${MACHINE_CPUARCH} == "i386" _ena= ena .if ${MK_OFED} != "no" || defined(ALL_MODULES) _ibcore= ibcore _ipoib= ipoib _iser= iser .endif _ipmi= ipmi _iwlwifi= iwlwifi .if ${MK_SOURCELESS_UCODE} != "no" _iwlwififw= iwlwififw .endif _mlx4= mlx4 _mlx5= mlx5 .if (${MK_INET_SUPPORT} != "no" && ${MK_INET6_SUPPORT} != "no") || \ defined(ALL_MODULES) _mlx4en= mlx4en _mlx5en= mlx5en .endif .if ${MK_OFED} != "no" || defined(ALL_MODULES) _mthca= mthca _mlx4ib= mlx4ib _mlx5ib= mlx5ib .endif _ossl= ossl _rtw88= rtw88 .if ${MK_SOURCELESS_UCODE} != "no" _rtw88fw= rtw88fw .endif _vmware= vmware .endif # MAC framework .if ${KERN_OPTS:MMAC} || defined(ALL_MODULES) _mac_biba= mac_biba _mac_bsdextended= mac_bsdextended _mac_ifoff= mac_ifoff _mac_lomac= mac_lomac _mac_mls= mac_mls _mac_none= mac_none _mac_ntpd= mac_ntpd _mac_partition= mac_partition _mac_pimd= mac_pimd _mac_portacl= mac_portacl _mac_priority= mac_priority _mac_seeotheruids= mac_seeotheruids _mac_stub= mac_stub _mac_test= mac_test .endif .if ${MK_NETGRAPH} != "no" || defined(ALL_MODULES) _netgraph= netgraph .endif .if (${MK_PF} != "no" && (${MK_INET_SUPPORT} != "no" || \ ${MK_INET6_SUPPORT} != "no")) || defined(ALL_MODULES) _pf= pf _pflog= pflog .if ${MK_INET_SUPPORT} != "no" _pfsync= pfsync .endif .endif .if ${MK_SOURCELESS_UCODE} != "no" _bce= bce _fxp= fxp _ispfw= ispfw _ti= ti .if ${MACHINE_CPUARCH} != "mips" _mwlfw= mwlfw _otusfw= otusfw _ralfw= ralfw _rtwnfw= rtwnfw .endif .endif .if ${MK_SOURCELESS_UCODE} != "no" && ${MACHINE_CPUARCH} != "arm" && \ ${MACHINE_CPUARCH} != "mips" && \ ${MACHINE_ARCH} != "powerpc" && ${MACHINE_ARCH} != "powerpcspe" && \ ${MACHINE_CPUARCH} != "riscv" _cxgbe= cxgbe .endif .if ${MACHINE_ARCH} == "amd64" || ${MACHINE_ARCH} == "arm64" || ${MACHINE_ARCH:Mpowerpc64*} _ice= ice .if ${MK_SOURCELESS_UCODE} != "no" _ice_ddp= ice_ddp .endif .endif .if ${MACHINE_CPUARCH} == "aarch64" || ${MACHINE_CPUARCH} == "arm" || \ ${MACHINE_CPUARCH} == "riscv" _if_cgem= if_cgem .endif # These rely on 64bit atomics .if ${MACHINE_ARCH} != "powerpc" && ${MACHINE_ARCH} != "powerpcspe" && \ ${MACHINE_CPUARCH} != "mips" _mps= mps _mpr= mpr .endif .if ${MK_TESTS} != "no" || defined(ALL_MODULES) SUBDIR+= tests .endif .if ${MK_ZFS} != "no" || (defined(ALL_MODULES) && ${MACHINE_CPUARCH} != "powerpc") SUBDIR+= zfs .endif .if (${MACHINE_CPUARCH} == "mips" && ${MACHINE_ARCH:Mmips64} == "") _hwpmc_mips24k= hwpmc_mips24k _hwpmc_mips74k= hwpmc_mips74k .endif .if ${MACHINE_CPUARCH} != "aarch64" && ${MACHINE_CPUARCH} != "arm" && \ ${MACHINE_CPUARCH} != "mips" && ${MACHINE_CPUARCH} != "powerpc" && \ ${MACHINE_CPUARCH} != "riscv" _syscons= syscons .endif .if ${MACHINE_CPUARCH} != "mips" # no BUS_SPACE_UNSPECIFIED # No barrier instruction support (specific to this driver) _sym= sym # intr_disable() is a macro, causes problems .if ${MK_SOURCELESS_UCODE} != "no" _cxgb= cxgb .endif .endif .if ${MACHINE_CPUARCH} == "aarch64" _allwinner= allwinner _armv8crypto= armv8crypto +_dpaa2= dpaa2 _dwwdt= dwwdt _em= em _rockchip= rockchip .endif .if ${MACHINE_CPUARCH} == "aarch64" || ${MACHINE_CPUARCH} == "arm" _sdhci_fdt= sdhci_fdt .endif .if ${MACHINE_CPUARCH} == "i386" || ${MACHINE_CPUARCH} == "amd64" _agp= agp _an= an .if ${MACHINE_CPUARCH} == "i386" || !empty(COMPAT_FREEBSD32_ENABLED) _aout= aout .endif _bios= bios .if ${MK_SOURCELESS_UCODE} != "no" _bxe= bxe .endif _cardbus= cardbus _cbb= cbb _cpuctl= cpuctl _cpufreq= cpufreq _dpms= dpms _em= em _et= et _ftwd= ftwd _exca= exca _if_ndis= if_ndis _igc= igc _io= io _itwd= itwd _ix= ix _ixv= ixv .if ${MK_SOURCELESS_UCODE} != "no" _lio= lio .endif _mgb= mgb _nctgpio= nctgpio _ndis= ndis _ntb= ntb _ocs_fc= ocs_fc _pccard= pccard _qat= qat _qatfw= qatfw .if ${MK_OFED} != "no" || defined(ALL_MODULES) _rdma= rdma .endif _safe= safe _speaker= speaker _splash= splash _sppp= sppp _wbwd= wbwd _aac= aac _aacraid= aacraid _acpi= acpi .if ${MK_CRYPT} != "no" || defined(ALL_MODULES) _aesni= aesni .endif _amd_ecc_inject=amd_ecc_inject _amdsbwd= amdsbwd _amdsmn= amdsmn _amdtemp= amdtemp _arcmsr= arcmsr _asmc= asmc .if ${MK_CRYPT} != "no" || defined(ALL_MODULES) _blake2= blake2 .endif _bytgpio= bytgpio _chvgpio= chvgpio _ciss= ciss _chromebook_platform= chromebook_platform _coretemp= coretemp .if ${MK_SOURCELESS_HOST} != "no" && empty(KCSAN_ENABLED) _hpt27xx= hpt27xx .endif _hptiop= hptiop .if ${MK_SOURCELESS_HOST} != "no" && empty(KCSAN_ENABLED) _hptmv= hptmv _hptnr= hptnr _hptrr= hptrr .endif _hyperv= hyperv _ichwd= ichwd _ida= ida _iir= iir _intelspi= intelspi _ips= ips _isci= isci _ipw= ipw _iwi= iwi _iwm= iwm _iwn= iwn .if ${MK_SOURCELESS_UCODE} != "no" _ipwfw= ipwfw _iwifw= iwifw _iwmfw= iwmfw _iwnfw= iwnfw .endif _mly= mly _nfe= nfe _nvd= nvd _nvme= nvme _nvram= nvram .if ${MK_CRYPT} != "no" || defined(ALL_MODULES) _padlock= padlock _padlock_rng= padlock_rng _rdrand_rng= rdrand_rng .endif _pchtherm = pchtherm _s3= s3 _sdhci_acpi= sdhci_acpi _superio= superio _tpm= tpm _twa= twa _vesa= vesa _viawd= viawd _vmd= vmd _wpi= wpi .if ${MK_SOURCELESS_UCODE} != "no" _wpifw= wpifw .endif _x86bios= x86bios .endif .if ${MACHINE_CPUARCH} == "amd64" _amdgpio= amdgpio _ccp= ccp _iavf= iavf _ioat= ioat _ixl= ixl _nvdimm= nvdimm _pms= pms _qlxge= qlxge _qlxgb= qlxgb _sume= sume .if ${MK_SOURCELESS_UCODE} != "no" _qlxgbe= qlxgbe _qlnx= qlnx .endif _sfxge= sfxge _sgx= sgx _sgx_linux= sgx_linux _smartpqi= smartpqi .if ${MK_BHYVE} != "no" || defined(ALL_MODULES) .if ${KERN_OPTS:MSMP} _vmm= vmm .endif .endif .endif .if ${MACHINE_CPUARCH} == "i386" # XXX some of these can move to the general case when de-i386'ed # XXX some of these can move now, but are untested on other architectures. _3dfx= 3dfx _3dfx_linux= 3dfx_linux .if ${MK_SOURCELESS_HOST} != "no" _ce= ce .endif .if ${MK_SOURCELESS_HOST} != "no" _cp= cp .endif _glxiic= glxiic _glxsb= glxsb _pcfclock= pcfclock _pst= pst _sbni= sbni .endif .if ${MACHINE_ARCH} == "armv7" _cfi= cfi _cpsw= cpsw .endif .if ${MACHINE_CPUARCH} == "powerpc" _aacraid= aacraid _agp= agp _an= an _cardbus= cardbus _cbb= cbb _cfi= cfi _cpufreq= cpufreq _exca= exca _ffec= ffec _nvd= nvd _nvme= nvme _pccard= pccard .endif .if ${MACHINE_ARCH:Mpowerpc64*} != "" _ipmi= ipmi _ixl= ixl _nvram= opal_nvram .endif .if ${MACHINE_CPUARCH} == "powerpc" && ${MACHINE_ARCH} != "powerpcspe" # Don't build powermac_nvram for powerpcspe, it's never supported. _nvram+= powermac_nvram .endif .if (${MACHINE_CPUARCH} == "aarch64" || ${MACHINE_CPUARCH} == "amd64" || \ ${MACHINE_ARCH:Marmv[67]*} != "" || ${MACHINE_CPUARCH} == "i386") _cloudabi32= cloudabi32 .endif .if ${MACHINE_CPUARCH} == "aarch64" || ${MACHINE_CPUARCH} == "amd64" _cloudabi64= cloudabi64 .endif .endif .if ${MACHINE_ARCH:Marmv[67]*} != "" || ${MACHINE_CPUARCH} == "aarch64" _bcm283x_clkman= bcm283x_clkman _bcm283x_pwm= bcm283x_pwm .endif .if !(${COMPILER_TYPE} == "clang" && ${COMPILER_VERSION} < 110000) # LLVM 10 crashes when building if_malo_pci.c, fixed in LLVM11: # https://bugs.llvm.org/show_bug.cgi?id=44351 _malo= malo .endif SUBDIR+=${MODULES_EXTRA} .for reject in ${WITHOUT_MODULES} SUBDIR:= ${SUBDIR:N${reject}} .endfor # Calling kldxref(8) for each module is expensive. .if !defined(NO_XREF) .MAKEFLAGS+= -DNO_XREF afterinstall: .PHONY @if type kldxref >/dev/null 2>&1; then \ ${ECHO} ${KLDXREF_CMD} ${DESTDIR}${KMODDIR}; \ ${KLDXREF_CMD} ${DESTDIR}${KMODDIR}; \ fi .endif SUBDIR:= ${SUBDIR:u:O} .include diff --git a/sys/modules/dpaa2/Makefile b/sys/modules/dpaa2/Makefile new file mode 100644 index 000000000000..eeb9e7f340bf --- /dev/null +++ b/sys/modules/dpaa2/Makefile @@ -0,0 +1,42 @@ +.PATH: ${SRCTOP}/sys/dev/dpaa2 + +KMOD= dpaa2 + +SRCS= dpaa2_mc.c +SRCS+= dpaa2_rc.c +SRCS+= dpaa2_io.c +SRCS+= dpaa2_bp.c +SRCS+= dpaa2_ni.c +SRCS+= dpaa2_mcp.c +SRCS+= dpaa2_swp.c +SRCS+= dpaa2_mac.c +SRCS+= dpaa2_con.c + +SRCS+= dpaa2_cmd_if.c dpaa2_cmd_if.h +SRCS+= dpaa2_swp_if.c dpaa2_swp_if.h +SRCS+= dpaa2_mc_if.c dpaa2_mc_if.h +SRCS+= memac_mdio_common.c memac_mdio_if.c memac_mdio_if.h + +SRCS+= bus_if.h device_if.h miibus_if.h +SRCS+= pcib_if.h pci_if.h +SRCS+= opt_acpi.h opt_platform.h + +.if !empty(OPT_ACPI) +SRCS+= dpaa2_mc_acpi.c \ + memac_mdio_acpi.c \ + acpi_if.h \ + acpi_bus_if.h +.endif + +.if !empty(OPT_FDT) +SRCS+= dpaa2_mc_fdt.c \ + memac_mdio_fdt.c \ + ofw_bus_if.h +.endif + +MFILES= dev/dpaa2/dpaa2_cmd_if.m \ + dev/dpaa2/dpaa2_swp_if.m \ + dev/dpaa2/dpaa2_mc_if.m \ + dev/dpaa2/memac_mdio_if.m + +.include