'p' 80-9F linux/ppdev.h user-space parport
'p' A1-A5 linux/pps.h LinuxPPS
+'p' B1-B3 linux/pps_gen.h LinuxPPS
'q' 00-1F linux/serio.h
'q' 80-FF linux/telephony.h Internet PhoneJACK, Internet LineJACK
'z' 10-4F drivers/s390/crypto/zcrypt_api.h conflict!
'|' 00-7F linux/media.h
+ '|' 80-9F samples/ Any sample and example drivers
0x80 00-1F linux/fb.h
0x81 00-1F linux/vduse.h
0x89 00-06 arch/x86/include/asm/sockios.h
F: Documentation/scsi/aacraid.rst
F: drivers/scsi/aacraid/
+AAEON UPBOARD FPGA MFD DRIVER
+S: Maintained
+F: drivers/leds/leds-upboard.c
+F: drivers/mfd/upboard-fpga.c
+F: drivers/pinctrl/pinctrl-upboard.c
+F: include/linux/mfd/upboard-fpga.h
+
AB8500 BATTERY AND CHARGER DRIVERS
F: Documentation/devicetree/bindings/power/supply/*ab8500*
ALLWINNER A31 CSI DRIVER
S: Maintained
T: git git://linuxtv.org/media.git
F: drivers/media/platform/sunxi/sun6i-csi/
ALLWINNER A31 ISP DRIVER
S: Maintained
T: git git://linuxtv.org/media.git
F: drivers/staging/media/sunxi/sun6i-isp/uapi/sun6i-isp-config.h
ALLWINNER A31 MIPI CSI-2 BRIDGE DRIVER
S: Maintained
T: git git://linuxtv.org/media.git
ALLWINNER VPU DRIVER
S: Maintained
F: drivers/staging/media/sunxi/cedrus/
-S: Supported
+S: Maintained
F: Documentation/networking/device_drivers/ethernet/amazon/ena.rst
F: drivers/net/ethernet/amazon/
S: Supported
F: drivers/i2c/busses/i2c-amd-asf-plat.c
+AMD NODE DRIVER
+S: Supported
+F: arch/x86/include/asm/amd_node.h
+F: arch/x86/kernel/amd_node.c
+
AMD PDS CORE DRIVER
-S: Supported
+S: Maintained
F: Documentation/networking/device_drivers/ethernet/amd/pds_core.rst
F: drivers/net/ethernet/amd/pds_core/
F: include/linux/pds/
AMD PMC DRIVER
-S: Maintained
+S: Supported
F: drivers/platform/x86/amd/pmc/
AMD PMF DRIVER
S: Supported
F: drivers/spi/spi-amd.c
+AMD XDNA DRIVER
+S: Supported
+T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
+F: Documentation/accel/amdxdna/
+F: drivers/accel/amdxdna/
+F: include/trace/events/amdxdna.h
+F: include/uapi/drm/amdxdna_accel.h
+
AMD XGBE DRIVER
-S: Supported
+S: Maintained
F: arch/arm64/boot/dts/amd/amd-seattle-xgbe*.dtsi
F: drivers/net/ethernet/amd/xgbe/
F: Documentation/devicetree/bindings/rtc/amlogic,a4-rtc.yaml
F: drivers/rtc/rtc-amlogic-a4.c
-AMPHENOL CHIPCAP 2 HUMIDITY-TEMPERATURE IIO DRIVER
+AMPHENOL CHIPCAP 2 DRIVER
S: Maintained
F: Documentation/devicetree/bindings/iio/adc/adi,ad4695.yaml
F: Documentation/iio/ad4695.rst
F: drivers/iio/adc/ad4695.c
-F: include/dt-bindings/iio/adi,ad4695.h
+F: include/dt-bindings/iio/adc/adi,ad4695.h
ANALOG DEVICES INC AD7091R DRIVER
APPLIED MICRO (APM) X-GENE SOC ETHERNET (V2) DRIVER
-S: Supported
+S: Maintained
F: drivers/net/ethernet/apm/xgene-v2/
APPLIED MICRO (APM) X-GENE SOC ETHERNET DRIVER
-S: Supported
+S: Maintained
F: Documentation/devicetree/bindings/net/apm-xgene-enet.txt
F: Documentation/devicetree/bindings/net/apm-xgene-mdio.txt
F: drivers/net/ethernet/apm/xgene/
AQUANTIA ETHERNET DRIVER (atlantic)
-S: Supported
+S: Maintained
W: https://www.marvell.com/
Q: https://patchwork.kernel.org/project/netdevbpf/list/
F: Documentation/networking/device_drivers/ethernet/aquantia/atlantic.rst
AQUANTIA ETHERNET DRIVER PTP SUBSYSTEM
-S: Supported
+S: Maintained
W: http://www.aquantia.com
F: drivers/net/ethernet/aquantia/atlantic/aq_ptp*
S: Maintained
F: Documentation/devicetree/bindings/arm/pmu.yaml
F: Documentation/devicetree/bindings/perf/
F: drivers/clk/clk-bm1880.c
F: drivers/pinctrl/pinctrl-bm1880.c
+ARM/BLAIZE ARCHITECTURE
+S: Maintained
+F: Documentation/devicetree/bindings/arm/blaize.yaml
+F: arch/arm64/boot/dts/blaize/
+
ARM/CALXEDA HIGHBANK ARCHITECTURE
ARM/CAVIUM THUNDER NETWORK DRIVER
-S: Supported
+S: Maintained
F: drivers/net/ethernet/cavium/thunder/
ARM/CIRRUS LOGIC BK3 MACHINE SUPPORT
S: Maintained
F: arch/arm64/boot/dts/freescale/s32g*.dts*
F: drivers/pinctrl/nxp/
+ARM/NXP S32G/S32R DWMAC ETHERNET DRIVER
+S: Maintained
+F: Documentation/devicetree/bindings/net/nxp,s32-dwmac.yaml
+F: drivers/net/ethernet/stmicro/stmmac/dwmac-s32.c
+
ARM/Orion SoC/Technologic Systems TS-78xx platform support
F: drivers/*/*s3c64xx*
F: drivers/*/*s5pv210*
F: drivers/clocksource/samsung_pwm_timer.c
+F: drivers/mailbox/exynos-mailbox.c
F: drivers/memory/samsung/
F: drivers/pwm/pwm-samsung.c
F: drivers/soc/samsung/
ATMEL MACB ETHERNET DRIVER
-S: Supported
+S: Maintained
F: drivers/net/ethernet/cadence/
ATMEL MAXTOUCH DRIVER
F: drivers/platform/x86/barco-p50-gpio.c
BATMAN ADVANCED
-M: Antonio Quartulli <a@unstable.cc>
S: Maintained
BROADCOM B44 10/100 ETHERNET DRIVER
-S: Supported
+S: Maintained
F: drivers/net/ethernet/broadcom/b44.*
BROADCOM B53/SF2 ETHERNET SWITCH DRIVER
-S: Supported
+S: Maintained
F: drivers/net/ethernet/broadcom/bnx2.*
F: drivers/net/ethernet/broadcom/bnx2_*
-S: Supported
+S: Maintained
F: drivers/net/ethernet/broadcom/bnx2x/
BROADCOM BNXT_EN 50 GIGABIT ETHERNET DRIVER
-S: Supported
+S: Maintained
F: drivers/firmware/broadcom/tee_bnxt_fw.c
F: drivers/net/ethernet/broadcom/bnxt/
F: include/linux/firmware/broadcom/tee_bnxt_fw.h
-S: Supported
+S: Maintained
F: Documentation/devicetree/bindings/net/brcm,bcmgenet.yaml
F: Documentation/devicetree/bindings/net/brcm,unimac-mdio.yaml
F: drivers/net/ethernet/broadcom/genet/
BROADCOM NETXTREME-E ROCE DRIVER
S: Supported
W: http://www.broadcom.com
-S: Supported
+S: Maintained
F: Documentation/devicetree/bindings/net/brcm,systemport.yaml
F: drivers/net/ethernet/broadcom/bcmsysport.*
F: drivers/net/ethernet/broadcom/unimac.h
-S: Supported
+S: Maintained
F: drivers/net/ethernet/broadcom/tg3.*
BROADCOM VK DRIVER
-S: Supported
+S: Maintained
F: drivers/net/ethernet/brocade/bna/
BSG (block layer generic sg v4 driver)
CADENCE NAND DRIVER
S: Orphan
-F: Documentation/devicetree/bindings/mtd/cadence-nand-controller.txt
+F: Documentation/devicetree/bindings/mtd/cdns,hp-nfc.yaml
F: drivers/mtd/nand/raw/cadence-nand-controller.c
CADENCE USB3 DRD IP DRIVER
F: include/uapi/linux/can/isotp.h
F: include/uapi/linux/can/raw.h
F: net/can/
+F: net/sched/em_canid.c
CAN-J1939 NETWORK LAYER
S: Supported
F: include/linux/capability.h
+F: include/trace/events/capability.h
F: include/uapi/linux/capability.h
F: kernel/capability.c
F: security/commoncap.c
F: drivers/char/
F: drivers/misc/
F: include/linux/miscdevice.h
+ F: samples/rust/rust_misc_device.rs
X: drivers/char/agp/
X: drivers/char/hw_random/
X: drivers/char/ipmi/
CHROMEOS EC USB TYPE-C DRIVER
S: Maintained
F: drivers/platform/chrome/cros_ec_typec.*
+F: drivers/platform/chrome/cros_typec_altmode.*
F: drivers/platform/chrome/cros_typec_switch.c
F: drivers/platform/chrome/cros_typec_vdm.*
S: Maintained
F: drivers/watchdog/cros_ec_wdt.c
+CHROMEOS UCSI DRIVER
+S: Maintained
+F: drivers/usb/typec/ucsi/cros_ec_ucsi.c
+
CHRONTEL CH7322 CEC DRIVER
S: Maintained
F: Documentation/devicetree/bindings/sound/cirrus,cs*
+F: Documentation/sound/codecs/cs*
F: drivers/mfd/cs42l43*
F: drivers/pinctrl/cirrus/pinctrl-cs42l43*
F: drivers/spi/spi-cs42l43*
S: Supported
W: https://github.com/CirrusLogic/linux-drivers/wiki
T: git https://github.com/CirrusLogic/linux-drivers.git
-F: drivers/firmware/cirrus/*
-F: include/linux/firmware/cirrus/*
+F: drivers/firmware/cirrus/
+F: include/linux/firmware/cirrus/
CIRRUS LOGIC EP93XX ETHERNET DRIVER
CISCO VIC ETHERNET NIC DRIVER
-S: Supported
+S: Maintained
F: drivers/net/ethernet/cisco/enic/
CISCO VIC LOW LATENCY NIC DRIVER
F: Documentation/filesystems/cramfs.rst
F: fs/cramfs/
+CRC LIBRARY
+S: Maintained
+T: git https://git.kernel.org/pub/scm/linux/kernel/git/ebiggers/linux.git crc-next
+F: Documentation/staging/crc*
+F: arch/*/lib/crc*
+F: include/linux/crc*
+F: lib/crc*
+
CREATIVE SB0540
S: Maintained
F: drivers/hid/hid-creative-sb0540.c
+INTEL CRPS COMMON REDUNDANT PSU DRIVER
+S: Maintained
+F: Documentation/hwmon/crps.rst
+F: drivers/hwmon/pmbus/crps.c
+
CRYPTO API
CXGB3 ETHERNET DRIVER (CXGB3)
-S: Supported
+S: Maintained
W: http://www.chelsio.com
F: drivers/net/ethernet/chelsio/cxgb3/
CXGB4 ETHERNET DRIVER (CXGB4)
-S: Supported
+S: Maintained
W: http://www.chelsio.com
F: drivers/net/ethernet/chelsio/cxgb4/
CXGB4 INLINE CRYPTO DRIVER
-S: Supported
+S: Maintained
W: http://www.chelsio.com
F: drivers/net/ethernet/chelsio/inline_crypto/
CXGB4VF ETHERNET DRIVER (CXGB4VF)
-S: Supported
+S: Maintained
W: http://www.chelsio.com
F: drivers/net/ethernet/chelsio/cxgb4vf/
-S: Supported
-F: Documentation/ABI/testing/sysfs-class-cxl
+S: Obsolete
+F: Documentation/ABI/obsolete/sysfs-class-cxl
F: Documentation/arch/powerpc/cxl.rst
F: arch/powerpc/platforms/powernv/pci-cxl.c
F: drivers/misc/cxl/
F: include/linux/damon.h
F: include/trace/events/damon.h
F: mm/damon/
+F: samples/damon/
F: tools/testing/selftests/damon/
DAVICOM FAST ETHERNET (DMFE) NETWORK DRIVER
S: Maintained
F: drivers/rtc/rtc-sd2405al.c
-DH ELECTRONICS IMX6 DHCOM/DHCOR BOARD SUPPORT
+DH ELECTRONICS DHSOM SOM AND BOARD SUPPORT
-S: Maintained
-F: arch/arm/boot/dts/nxp/imx/imx6*-dhcom-*
-F: arch/arm/boot/dts/nxp/imx/imx6*-dhcor-*
-
-DH ELECTRONICS STM32MP1 DHCOM/DHCOR BOARD SUPPORT
S: Maintained
-F: arch/arm/boot/dts/st/stm32mp1*-dhcom-*
-F: arch/arm/boot/dts/st/stm32mp1*-dhcor-*
+N: dhcom
+N: dhcor
+N: dhsom
DIALOG SEMICONDUCTOR DRIVERS
DRIVER CORE, KOBJECTS, DEBUGFS AND SYSFS
S: Supported
T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core.git
F: Documentation/core-api/kobject.rst
F: include/linux/fwnode.h
F: include/linux/kobj*
F: include/linux/property.h
+ F: include/linux/sysfs.h
F: lib/kobj*
F: rust/kernel/device.rs
+ F: rust/kernel/device_id.rs
+ F: rust/kernel/devres.rs
+ F: rust/kernel/driver.rs
+ F: rust/kernel/platform.rs
+ F: samples/rust/rust_driver_platform.rs
DRIVERS FOR OMAP ADAPTIVE VOLTAGE SCALING (AVS)
F: drivers/gpu/drm/sun4i/sun8i*
DRM DRIVER FOR ARM PL111 CLCD
-S: Orphan
+S: Maintained
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
F: drivers/gpu/drm/pl111/
F: drivers/gpu/drm/panel/panel-lg-sw43408.c
DRM DRIVER FOR LOGICVC DISPLAY CONTROLLER
S: Supported
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
F: drivers/gpu/drm/logicvc/
S: Obsolete
W: https://www.kraxel.org/blog/2014/10/qemu-using-cirrus-considered-harmful/
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
-F: drivers/gpu/drm/tiny/cirrus.c
+F: drivers/gpu/drm/tiny/cirrus-qemu.c
DRM DRIVER FOR QXL VIRTUAL GPU
DRM DRIVERS FOR STI
S: Maintained
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
-S: Supported
+S: Maintained
W: http://www.emulex.com
F: drivers/net/ethernet/emulex/benet/
F: rust/kernel/net/phy/reg.rs
EXEC & BINFMT API, ELF
S: Supported
T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git for-next/execve
F: fs/tests/exec_kunit.c
F: include/linux/binfmts.h
F: include/linux/elf.h
+F: include/uapi/linux/auxvec.h
F: include/uapi/linux/binfmts.h
F: include/uapi/linux/elf.h
F: tools/testing/selftests/exec/
S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/chanwoo/extcon.git
F: Documentation/devicetree/bindings/extcon/
+F: Documentation/driver-api/extcon.rst
F: Documentation/firmware-guide/acpi/extcon-intel-int3496.rst
F: drivers/extcon/
F: include/linux/extcon.h
S: Maintained
F: drivers/input/joystick/fsia6b.c
-FOCUSRITE SCARLETT2 MIXER DRIVER (Scarlett Gen 2+ and Clarett)
+FOCUSRITE CONTROL PROTOCOL/SCARLETT2 MIXER DRIVERS (Scarlett Gen 2+, Clarett, and Vocaster)
S: Maintained
-W: https://github.com/geoffreybennett/scarlett-gen2
-B: https://github.com/geoffreybennett/scarlett-gen2/issues
-T: git https://github.com/geoffreybennett/scarlett-gen2.git
+W: https://github.com/geoffreybennett/linux-fcp
+B: https://github.com/geoffreybennett/linux-fcp/issues
+T: git https://github.com/geoffreybennett/linux-fcp.git
+F: include/uapi/sound/fcp.h
F: include/uapi/sound/scarlett2.h
+F: sound/usb/fcp.c
F: sound/usb/mixer_scarlett2.c
FORCEDETH GIGABIT ETHERNET DRIVER
FUNGIBLE ETHERNET DRIVERS
-S: Supported
+S: Maintained
F: drivers/net/ethernet/fungible/
FUSE: FILESYSTEM IN USERSPACE
-S: Supported
+S: Maintained
F: Documentation/networking/device_drivers/ethernet/google/gve.rst
F: drivers/net/ethernet/google
GOOGLE TENSOR SoC SUPPORT
S: Maintained
+C: irc://irc.oftc.net/pixel6-kernel-dev
F: Documentation/devicetree/bindings/clock/google,gs101-clock.yaml
F: arch/arm64/boot/dts/exynos/google/
F: drivers/clk/samsung/clk-gs101.c
F: net/handshake/
HANTRO VPU CODEC DRIVER
HIPPI
S: Maintained
F: drivers/net/hippi/
F: include/linux/hippidevice.h
F: Documentation/networking/device_drivers/ethernet/microsoft/netvsc.rst
F: Documentation/virt/hyperv
F: arch/arm64/hyperv
-F: arch/arm64/include/asm/hyperv-tlfs.h
F: arch/arm64/include/asm/mshyperv.h
F: arch/x86/hyperv
-F: arch/x86/include/asm/hyperv-tlfs.h
F: arch/x86/include/asm/mshyperv.h
F: arch/x86/include/asm/trace/hyperv.h
F: arch/x86/kernel/cpu/mshyperv.c
F: drivers/scsi/storvsc_drv.c
F: drivers/uio/uio_hv_generic.c
F: drivers/video/fbdev/hyperv_fb.c
-F: include/asm-generic/hyperv-tlfs.h
F: include/asm-generic/mshyperv.h
F: include/clocksource/hyperv_timer.h
+F: include/hyperv/hvgdk.h
+F: include/hyperv/hvgdk_ext.h
+F: include/hyperv/hvgdk_mini.h
+F: include/hyperv/hvhdk.h
+F: include/hyperv/hvhdk_mini.h
F: include/linux/hyperv.h
F: include/net/mana
F: include/uapi/linux/hyperv.h
-S: Supported
+S: Maintained
F: drivers/net/ethernet/ibm/ibmvnic.*
IBM Power VFIO Support
IBM Power Virtual Ethernet Device Driver
-S: Supported
+S: Maintained
F: drivers/net/ethernet/ibm/ibmveth.*
IBM Power Virtual FC Device Drivers
F: include/linux/mfd/intel_pmc_bxt.h
INTEL C600 SERIES SAS CONTROLLER DRIVER
-S: Supported
+S: Orphan
T: git git://git.code.sf.net/p/intel-sas/isci
F: drivers/scsi/isci/
-S: Supported
+S: Maintained
W: https://www.intel.com/content/www/us/en/support.html
Q: https://patchwork.ozlabs.org/project/intel-wired-lan/list/
T: git git://git.kernel.org/pub/scm/linux/kernel/git/tnguy/net-queue.git
F: arch/x86/include/asm/intel_telemetry.h
F: drivers/platform/x86/intel/telemetry/
+INTEL TOUCH HOST CONTROLLER (THC) DRIVER
+S: Maintained
+F: drivers/hid/intel-thc-hid/
+
INTEL TPMI DRIVER
F: io_uring/
IPMI SUBSYSTEM
S: Supported
W: http://openipmi.sourceforge.net/
F: scripts/Kconfig.include
F: scripts/kconfig/
+KCORE
+S: Maintained
+F: fs/proc/kcore.c
+F: include/linux/kcore.h
+
KCOV
F: arch/arm64/include/uapi/asm/kvm*
F: arch/arm64/kvm/
F: include/kvm/arm_*
-F: tools/testing/selftests/kvm/*/aarch64/
-F: tools/testing/selftests/kvm/aarch64/
+F: tools/testing/selftests/kvm/*/arm64/
+F: tools/testing/selftests/kvm/arm64/
KERNEL VIRTUAL MACHINE FOR LOONGARCH (KVM/LoongArch)
F: arch/s390/mm/gmap.c
F: drivers/s390/char/uvdevice.c
F: tools/testing/selftests/drivers/s390x/uvdevice/
-F: tools/testing/selftests/kvm/*/s390x/
-F: tools/testing/selftests/kvm/s390x/
+F: tools/testing/selftests/kvm/*/s390/
+F: tools/testing/selftests/kvm/s390/
KERNEL VIRTUAL MACHINE FOR X86 (KVM/x86)
F: arch/x86/include/uapi/asm/vmx.h
F: arch/x86/kvm/
F: arch/x86/kvm/*/
-F: tools/testing/selftests/kvm/*/x86_64/
-F: tools/testing/selftests/kvm/x86_64/
+F: tools/testing/selftests/kvm/*/x86/
+F: tools/testing/selftests/kvm/x86/
KERNFS
S: Maintained
W: http://kgdb.wiki.kernel.org/
T: git git://git.kernel.org/pub/scm/linux/kernel/git/jwessel/kgdb.git
-F: Documentation/dev-tools/kgdb.rst
+F: Documentation/process/debugging/kgdb.rst
F: drivers/misc/kgdbts.c
F: drivers/tty/serial/kgdboc.c
F: include/linux/kdb.h
-S: Supported
+S: Maintained
T: git https://github.com/alobakin/linux.git
F: drivers/net/ethernet/intel/libeth/
F: include/net/libeth/
-S: Supported
+S: Maintained
T: git https://github.com/alobakin/linux.git
F: drivers/net/ethernet/intel/libie/
F: include/linux/net/intel/libie/
S: Supported
-T: git git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu.git dev
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/rcu/linux.git rcu/dev
F: Documentation/atomic_bitops.txt
F: Documentation/atomic_t.txt
F: Documentation/core-api/refcount-vs-atomic.rst
S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git locking/core
F: include/linux/spinlock*.h
F: kernel/locking/
F: lib/locking*.[ch]
+F: rust/helpers/mutex.c
+F: rust/helpers/spinlock.c
+F: rust/kernel/sync/lock.rs
+F: rust/kernel/sync/lock/
+F: rust/kernel/sync/locked_by.rs
X: kernel/locking/locktorture.c
LOGICAL DISK MANAGER SUPPORT (LDM, Windows 2000/XP/Vista Dynamic Disks)
F: Documentation/devicetree/bindings/thermal/loongson,ls2k-thermal.yaml
F: drivers/thermal/loongson2_thermal.c
+LOONGSON EDAC DRIVER
+S: Maintained
+F: drivers/edac/loongson_edac.c
+
LSILOGIC MPT FUSION DRIVERS (FC/SAS/SPI)
-S: Supported
+S: Maintained
F: drivers/net/ethernet/marvell/octeon_ep
MARVELL OCTEON ENDPOINT VF DRIVER
-S: Supported
+S: Maintained
F: drivers/net/ethernet/marvell/octeon_ep_vf
MARVELL OCTEONTX2 PHYSICAL FUNCTION DRIVER
-S: Supported
+S: Maintained
F: drivers/net/ethernet/marvell/octeontx2/nic/
F: include/linux/soc/marvell/octeontx2/
-S: Supported
+S: Maintained
F: Documentation/networking/device_drivers/ethernet/marvell/octeontx2.rst
F: drivers/net/ethernet/marvell/octeontx2/af/
T: git git://linuxtv.org/media.git
F: drivers/media/dvb-frontends/stv6111*
+MEDIA DRIVERS FOR STM32 - CSI
+S: Supported
+T: git git://linuxtv.org/media_tree.git
+F: Documentation/devicetree/bindings/media/st,stm32mp25-csi.yaml
+F: drivers/media/platform/st/stm32/stm32-csi.c
+
MEDIA DRIVERS FOR STM32 - DCMI / DCMIPP
MELLANOX ETHERNET DRIVER (mlx4_en)
-S: Supported
+S: Maintained
W: https://www.nvidia.com/networking/
Q: https://patchwork.kernel.org/project/netdevbpf/list/
F: drivers/net/ethernet/mellanox/mlx4/en_*
-S: Supported
+S: Maintained
W: https://www.nvidia.com/networking/
Q: https://patchwork.kernel.org/project/netdevbpf/list/
F: drivers/net/ethernet/mellanox/mlx5/core/en_*
MELLANOX ETHERNET INNOVA DRIVERS
-S: Supported
+S: Maintained
W: https://www.nvidia.com/networking/
Q: https://patchwork.kernel.org/project/netdevbpf/list/
F: drivers/net/ethernet/mellanox/mlx5/core/en_accel/*
-S: Supported
+S: Maintained
W: https://www.nvidia.com/networking/
Q: https://patchwork.kernel.org/project/netdevbpf/list/
F: drivers/net/ethernet/mellanox/mlx4/
-S: Supported
+S: Maintained
W: https://www.nvidia.com/networking/
Q: https://patchwork.kernel.org/project/netdevbpf/list/
F: Documentation/networking/device_drivers/ethernet/mellanox/
S: Maintained
W: http://www.linux-mm.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
+F: mm/mlock.c
F: mm/mmap.c
+F: mm/mprotect.c
+F: mm/mremap.c
+F: mm/mseal.c
+F: mm/vma.c
+F: mm/vma.h
+F: mm/vma_internal.h
+F: tools/testing/vma/
MEMORY TECHNOLOGY DEVICES (MTD)
-S: Supported
+S: Maintained
F: Documentation/networking/device_drivers/ethernet/meta/
F: drivers/net/ethernet/meta/
S: Maintained
F: Documentation/networking/netconsole.rst
F: drivers/net/netconsole.c
-F: tools/testing/selftests/drivers/net/netcons_basic.sh
+F: tools/testing/selftests/drivers/net/lib/sh/lib_netcons.sh
+F: tools/testing/selftests/drivers/net/netcons\*
NETDEVSIM
NETERION 10GbE DRIVERS (s2io)
-S: Supported
+S: Maintained
F: Documentation/networking/device_drivers/ethernet/neterion/s2io.rst
F: drivers/net/ethernet/neterion/
F: include/linux/netdev*
F: include/linux/platform_data/wiznet.h
F: include/uapi/linux/cn_proc.h
-F: include/uapi/linux/ethtool_netlink.h
+F: include/uapi/linux/ethtool_netlink*
F: include/uapi/linux/if_*
F: include/uapi/linux/net_shaper.h
F: include/uapi/linux/netdev*
-S: Supported
+S: Maintained
F: drivers/net/ethernet/qlogic/netxen/
NET_FAILOVER MODULE
NITRO ENCLAVES (NE)
S: Supported
W: https://aws.amazon.com/ec2/nitro/nitro-enclaves/
F: Documentation/virt/ne_overview.rst
NITRO SECURE MODULE (NSM)
S: Supported
W: https://aws.amazon.com/ec2/nitro/nitro-enclaves/
F: drivers/misc/nsm.c
F: Documentation/filesystems/ntfs3.rst
F: fs/ntfs3/
+NTSYNC SYNCHRONIZATION PRIMITIVE DRIVER
+S: Supported
+F: Documentation/userspace-api/ntsync.rst
+F: drivers/misc/ntsync.c
+F: include/uapi/linux/ntsync.h
+F: tools/testing/selftests/drivers/ntsync/
+
NUBUS SUBSYSTEM
F: Documentation/ABI/stable/sysfs-bus-nvmem
F: Documentation/devicetree/bindings/nvmem/
F: drivers/nvmem/
+F: include/dt-bindings/nvmem/
F: include/linux/nvmem-consumer.h
F: include/linux/nvmem-provider.h
F: Documentation/ABI/testing/sysfs-firmware-ofw
F: drivers/of/
F: include/linux/of*.h
+ F: rust/kernel/of.rs
F: scripts/dtc/
F: tools/testing/selftests/dt/
K: of_overlay_notifier_
F: include/linux/packing.h
F: lib/packing.c
F: lib/packing_test.c
+F: scripts/gen_packed_field_checks.c
PADATA PARALLEL EXECUTION MECHANISM
S: Supported
-F: Documentation/devicetree/bindings/pci/mobiveil-pcie.txt
+F: Documentation/devicetree/bindings/pci/mbvl,gpex40-pcie.yaml
F: drivers/pci/controller/mobiveil/pcie-mobiveil*
PCI DRIVER FOR MVEBU (Marvell Armada 370 and Armada XP SOC support)
S: Maintained
-F: Documentation/devicetree/bindings/pci/layerscape-pcie-gen4.txt
F: drivers/pci/controller/mobiveil/pcie-layerscape-gen4.c
PCI DRIVER FOR PLDA PCIE IP
F: Documentation/misc-devices/pci-endpoint-test.rst
F: drivers/misc/pci_endpoint_test.c
F: drivers/pci/endpoint/
-F: tools/pci/
+F: tools/testing/selftests/pci_endpoint/
PCI ENHANCED ERROR HANDLING (EEH) FOR POWERPC
F: include/linux/of_pci.h
F: include/linux/pci*
F: include/uapi/linux/pci*
+ F: rust/kernel/pci.rs
+ F: samples/rust/rust_driver_pci.rs
PCIE BANDWIDTH CONTROLLER
-S: Supported
+S: Maintained
F: Documentation/networking/device_drivers/ethernet/pensando/ionic.rst
F: drivers/net/ethernet/pensando/
S: Maintained
F: Documentation/devicetree/bindings/pinctrl/fsl,*
F: Documentation/devicetree/bindings/pinctrl/nxp,s32*
S: Maintained
W: http://wiki.enneenne.com/index.php/LinuxPPS_support
F: Documentation/ABI/testing/sysfs-pps
+F: Documentation/ABI/testing/sysfs-pps-gen
F: Documentation/devicetree/bindings/pps/pps-gpio.yaml
F: Documentation/driver-api/pps.rst
F: drivers/pps/
F: include/linux/pps*.h
F: include/uapi/linux/pps.h
+F: include/uapi/linux/pps_gen.h
PRESSURE STALL INFORMATION (PSI)
QLOGIC QL4xxx ETHERNET DRIVER
-S: Supported
+S: Maintained
F: drivers/net/ethernet/qlogic/qed/
F: drivers/net/ethernet/qlogic/qede/
F: include/linux/qed/
QLOGIC QLA3XXX NETWORK DRIVER
-S: Supported
+S: Maintained
F: drivers/net/ethernet/qlogic/qla3xxx.*
QLOGIC QLA4XXX iSCSI DRIVER
-S: Supported
+S: Maintained
F: drivers/net/ethernet/qlogic/qlcnic/
QM1D1B0004 MEDIA DRIVER
S: Odd Fixes
F: drivers/media/tuners/qm1d1c0042*
+QNAP MCU DRIVER
+S: Maintained
+F: drivers/hwmon/qnap-mcu-hwmon.c
+F: drivers/input/misc/qnap-mcu-input.c
+F: drivers/leds/leds-qnap-mcu.c
+F: drivers/mfd/qnap-mcu.c
+F: include/linux/mfd/qnap-mcu.h
+
QNX4 FILESYSTEM
S: Maintained
RASPBERRY PI PISP BACK END
S: Maintained
F: Documentation/devicetree/bindings/media/raspberrypi,pispbe.yaml
S: Supported
-T: git git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu.git dev
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/rcu/linux.git rcu/dev
F: tools/testing/selftests/rcutorture
RDACM20 Camera Sensor
S: Supported
W: http://www.rdrop.com/users/paulmck/RCU/
-T: git git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu.git dev
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/rcu/linux.git rcu/dev
F: Documentation/RCU/
F: include/linux/rcu*
F: kernel/rcu/
+ F: rust/kernel/sync/rcu.rs
X: Documentation/RCU/torture.rst
X: include/linux/srcu*.h
X: kernel/rcu/srcu*.c
-S: Supported
+S: Maintained
F: Documentation/devicetree/bindings/net/renesas,etheravb.yaml
F: drivers/net/ethernet/renesas/Kconfig
F: drivers/net/ethernet/renesas/Makefile
-S: Supported
+S: Maintained
F: Documentation/devicetree/bindings/net/renesas,ethertsn.yaml
F: drivers/net/ethernet/renesas/rtsn.*
-S: Supported
+S: Maintained
F: Documentation/devicetree/bindings/net/renesas,ether.yaml
F: drivers/net/ethernet/renesas/Kconfig
F: drivers/net/ethernet/renesas/Makefile
RHASHTABLE
-L: netdev@vger.kernel.org
+L: linux-crypto@vger.kernel.org
S: Maintained
F: include/linux/rhashtable-types.h
F: include/linux/rhashtable.h
F: drivers/perf/riscv_pmu_legacy.c
F: drivers/perf/riscv_pmu_sbi.c
+RISC-V SPACEMIT SoC Support
+S: Maintained
+T: git https://github.com/spacemit-com/linux
+F: arch/riscv/boot/dts/spacemit/
+N: spacemit
+K: spacemit
+
RISC-V THEAD SoC SUPPORT
S: Odd Fixes
F: drivers/tty/serial/rp2.*
+ROHM BD79703 DAC
+S: Supported
+F: drivers/iio/dac/rohm-bd79703.c
+
ROHM BD99954 CHARGER IC
S: Supported
S: Supported
-F: drivers/iio/light/rohm-bu27008.c
F: drivers/iio/light/rohm-bu27034.c
ROHM MULTIFUNCTION BD9571MWV-M PMIC DEVICE DRIVERS
F: drivers/s390/crypto/
S390 ZFCP DRIVER
S: Supported
F: drivers/s390/scsi/zfcp_*
F: drivers/clk/samsung/clk-exynos850.c
F: include/dt-bindings/clock/exynos850.h
+SAMSUNG EXYNOS MAILBOX DRIVER
+S: Supported
+F: Documentation/devicetree/bindings/mailbox/google,gs101-mbox.yaml
+F: drivers/mailbox/exynos-mailbox.c
+F: include/linux/mailbox/exynos-message.h
+
SAMSUNG EXYNOS PSEUDO RANDOM NUMBER GENERATOR (RNG) DRIVER
SAMSUNG SXGBE DRIVERS
-S: Supported
+S: Maintained
F: drivers/net/ethernet/samsung/sxgbe/
SAMSUNG THERMAL DRIVER
S: Maintained
F: Documentation/devicetree/bindings/slimbus/
+F: Documentation/driver-api/slimbus.rst
F: drivers/slimbus/
F: include/linux/slimbus.h
-S: Supported
+S: Maintained
F: Documentation/networking/devlink/sfc.rst
F: drivers/net/ethernet/sfc/
S: Supported
W: http://www.rdrop.com/users/paulmck/RCU/
-T: git git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu.git dev
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/rcu/linux.git rcu/dev
F: include/linux/srcu*.h
F: kernel/rcu/srcu*.c
F: sound/soc/sof/
SOUND - GENERIC SOUND CARD (Simple-Audio-Card, Audio-Graph-Card)
S: Supported
F: Documentation/devicetree/bindings/media/i2c/st,st-mipid02.yaml
F: drivers/media/i2c/st-mipid02.c
+ST STC3117 FUEL GAUGE DRIVER
+S: Maintained
+F: Documentation/devicetree/bindings/power/supply/st,stc3117.yaml
+F: drivers/power/supply/stc3117_fuel_gauge.c
+
ST STM32 FIREWALL
S: Maintained
F: include/linux/jump_label*.h
F: include/linux/static_call*.h
F: kernel/jump_label.c
-F: kernel/static_call.c
+F: kernel/static_call*.c
STI AUDIO (ASoC) DRIVERS
SYNOPSYS DESIGNWARE ENTERPRISE ETHERNET DRIVER
-S: Supported
+S: Maintained
F: drivers/net/ethernet/synopsys/
SYNOPSYS DESIGNWARE ETHERNET XPCS DRIVER
TEHUTI ETHERNET DRIVER
-S: Supported
+S: Maintained
F: drivers/net/ethernet/tehuti/tehuti.*
TEHUTI TN40XX ETHERNET DRIVER
S: Maintained
F: Documentation/devicetree/bindings/hwmon/pmbus/ti,tps25990.yaml
+F: Documentation/hwmon/tps25990.rst
+F: drivers/hwmon/pmbus/tps25990.c
TEXAS INSTRUMENTS TPS23861 PoE PSE DRIVER
F: Documentation/devicetree/bindings/iio/dac/ti,dac7612.yaml
F: drivers/iio/dac/ti-dac7612.c
+TEXAS INSTRUMENTS' LB8864 LED BACKLIGHT DRIVER
+S: Maintained
+F: Documentation/devicetree/bindings/leds/backlight/ti,lp8864.yaml
+F: drivers/leds/leds-lp8864.c
+
TEXAS INSTRUMENTS' SYSTEM CONTROL INTERFACE (TISCI) PROTOCOL DRIVER
F: Documentation/devicetree/bindings/soc/ti/ti,pruss.yaml
F: drivers/pmdomain/ti/omap_prm.c
F: drivers/soc/ti/*
+F: include/linux/pruss_driver.h
TI LM49xxx FAMILY ASoC CODEC DRIVERS
S: Supported
-T: git git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu.git dev
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/rcu/linux.git rcu/dev
F: Documentation/RCU/torture.rst
F: kernel/locking/locktorture.c
F: kernel/rcu/rcuscale.c
S: Maintained
W: http://www.ideasonboard.org/uvc/
T: git git://linuxtv.org/media.git
+F: Documentation/userspace-api/media/drivers/uvcvideo.rst
+F: Documentation/userspace-api/media/v4l/metafmt-uvc.rst
+F: drivers/media/common/uvc.c
F: drivers/media/usb/uvc/
+F: include/linux/usb/uvc.h
F: include/uapi/linux/uvcvideo.h
USB WEBCAM GADGET
F: net/vmw_vsock/
F: tools/testing/vsock/
-VMA
-S: Maintained
-W: https://www.linux-mm.org
-T: git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
-F: mm/vma.c
-F: mm/vma.h
-F: mm/vma_internal.h
-F: tools/testing/vma/
-
VMALLOC
/*
* Allocate a buffer to hold the MC recoverable ranges.
*/
- mc_recoverable_range = memblock_alloc(size, __alignof__(u64));
- if (!mc_recoverable_range)
- panic("%s: Failed to allocate %u bytes align=0x%lx\n",
- __func__, size, __alignof__(u64));
+ mc_recoverable_range = memblock_alloc_or_panic(size, __alignof__(u64));
for (i = 0; i < mc_recoverable_range_len; i++) {
mc_recoverable_range[i].start_addr =
sysfs_bin_attr_init(attr);
attr->attr.name = name;
attr->attr.mode = 0400;
- attr->read = sysfs_bin_attr_simple_read;
+ attr->read_new = sysfs_bin_attr_simple_read;
attr->private = __va(vals[0]);
attr->size = vals[1];
}
err = blk_mq_alloc_sq_tag_set(&port->tag_set, &vdc_mq_ops,
- VDC_TX_RING_SIZE, BLK_MQ_F_SHOULD_MERGE);
+ VDC_TX_RING_SIZE, 0);
if (err)
return err;
char *type;
};
- static int vdc_device_probed(struct device *dev, void *arg)
+ static int vdc_device_probed(struct device *dev, const void *arg)
{
struct vio_dev *vdev = to_vio_dev(dev);
- struct vdc_check_port_data *port_data;
+ const struct vdc_check_port_data *port_data;
- port_data = (struct vdc_check_port_data *)arg;
+ port_data = (const struct vdc_check_port_data *)arg;
if ((vdev->dev_no == port_data->dev_no) &&
(!(strcmp((char *)&vdev->type, port_data->type))) &&
#include <linux/pm_runtime.h>
#include <linux/dma-mapping.h>
+#include <drm/clients/drm_client_setup.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
-#include <drm/drm_client_setup.h>
#include <drm/drm_drv.h>
#include <drm/drm_fbdev_dma.h>
#include <drm/drm_fourcc.h>
#define DRIVER_NAME "mediatek"
#define DRIVER_DESC "Mediatek SoC DRM"
-#define DRIVER_DATE "20150513"
#define DRIVER_MAJOR 1
#define DRIVER_MINOR 0
};
MODULE_DEVICE_TABLE(of, mtk_drm_of_ids);
- static int mtk_drm_match(struct device *dev, void *data)
+ static int mtk_drm_match(struct device *dev, const void *data)
{
if (!strncmp(dev_name(dev), "mediatek-drm", sizeof("mediatek-drm") - 1))
return true;
.name = DRIVER_NAME,
.desc = DRIVER_DESC,
- .date = DRIVER_DATE,
.major = DRIVER_MAJOR,
.minor = DRIVER_MINOR,
};
/* Thermal zone handling */
-/*
- * The complex conditional is necessary to avoid a cyclic dependency
- * between hwmon and thermal_sys modules.
- */
-#ifdef CONFIG_THERMAL_OF
static int hwmon_thermal_get_temp(struct thermal_zone_device *tz, int *temp)
{
struct hwmon_thermal_data *tdata = thermal_zone_device_priv(tz);
void *drvdata = dev_get_drvdata(dev);
int i;
+ if (!IS_ENABLED(CONFIG_THERMAL_OF))
+ return 0;
+
for (i = 1; info[i]; i++) {
int j;
struct hwmon_device *hwdev = to_hwmon_device(dev);
struct hwmon_thermal_data *tzdata;
+ if (!IS_ENABLED(CONFIG_THERMAL_OF))
+ return;
+
list_for_each_entry(tzdata, &hwdev->tzdata, node) {
if (tzdata->index == index) {
thermal_zone_device_update(tzdata->tzd,
}
}
-#else
-static int hwmon_thermal_register_sensors(struct device *dev)
-{
- return 0;
-}
-
-static void hwmon_thermal_notify(struct device *dev, int index) { }
-
-#endif /* IS_REACHABLE(CONFIG_THERMAL) && ... */
-
static int hwmon_attr_base(enum hwmon_sensor_types type)
{
if (type == hwmon_in || type == hwmon_intrusion)
static DEFINE_MUTEX(hwmon_pec_mutex);
- static int hwmon_match_device(struct device *dev, void *data)
+ static int hwmon_match_device(struct device *dev, const void *data)
{
return dev->class == &hwmon_class;
}
if (!dev)
return ERR_PTR(-EINVAL);
+ if (!name) {
+ name = devm_hwmon_sanitize_name(dev, dev_name(dev));
+ if (IS_ERR(name))
+ return ERR_CAST(name);
+ }
+
ptr = devres_alloc(devm_hwmon_release, sizeof(*ptr), GFP_KERNEL);
if (!ptr)
return ERR_PTR(-ENOMEM);
if (status)
goto out_remove_swnode;
+ client->debugfs = debugfs_create_dir(dev_name(&client->dev), adap->debugfs);
+
dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n",
client->name, dev_name(&client->dev));
if (ACPI_COMPANION(&client->dev))
acpi_device_clear_enumerated(ACPI_COMPANION(&client->dev));
+
+ debugfs_remove_recursive(client->debugfs);
device_remove_software_node(&client->dev);
device_unregister(&client->dev);
}
info.flags |= I2C_CLIENT_SLAVE;
}
+ info.flags |= I2C_CLIENT_USER;
+
client = i2c_new_client_device(adap, &info);
if (IS_ERR(client))
return PTR_ERR(client);
- /* Keep track of the added device */
- mutex_lock(&adap->userspace_clients_lock);
- list_add_tail(&client->detected, &adap->userspace_clients);
- mutex_unlock(&adap->userspace_clients_lock);
dev_info(dev, "%s: Instantiated device %s at 0x%02hx\n", "new_device",
info.type, info.addr);
}
static DEVICE_ATTR_WO(new_device);
- static int __i2c_find_user_addr(struct device *dev, void *addrp)
++static int __i2c_find_user_addr(struct device *dev, const void *addrp)
+{
+ struct i2c_client *client = i2c_verify_client(dev);
+ unsigned short addr = *(unsigned short *)addrp;
+
+ return client && client->flags & I2C_CLIENT_USER &&
+ i2c_encode_flags_to_addr(client) == addr;
+}
+
/*
* And of course let the users delete the devices they instantiated, if
* they got it wrong. This interface can only be used to delete devices
const char *buf, size_t count)
{
struct i2c_adapter *adap = to_i2c_adapter(dev);
- struct i2c_client *client, *next;
+ struct device *child_dev;
unsigned short addr;
char end;
int res;
return -EINVAL;
}
+ mutex_lock(&core_lock);
/* Make sure the device was added through sysfs */
- res = -ENOENT;
- mutex_lock_nested(&adap->userspace_clients_lock,
- i2c_adapter_depth(adap));
- list_for_each_entry_safe(client, next, &adap->userspace_clients,
- detected) {
- if (i2c_encode_flags_to_addr(client) == addr) {
- dev_info(dev, "%s: Deleting device %s at 0x%02hx\n",
- "delete_device", client->name, client->addr);
-
- list_del(&client->detected);
- i2c_unregister_device(client);
- res = count;
- break;
- }
+ child_dev = device_find_child(&adap->dev, &addr, __i2c_find_user_addr);
+ if (child_dev) {
+ i2c_unregister_device(i2c_verify_client(child_dev));
+ put_device(child_dev);
+ } else {
+ dev_err(dev, "Can't find userspace-created device at %#x\n", addr);
+ count = -ENOENT;
}
- mutex_unlock(&adap->userspace_clients_lock);
+ mutex_unlock(&core_lock);
- if (res < 0)
- dev_err(dev, "%s: Can't find device in list\n",
- "delete_device");
- return res;
+ return count;
}
static DEVICE_ATTR_IGNORE_LOCKDEP(delete_device, S_IWUSR, NULL,
delete_device_store);
adap->locked_flags = 0;
rt_mutex_init(&adap->bus_lock);
rt_mutex_init(&adap->mux_lock);
- mutex_init(&adap->userspace_clients_lock);
- INIT_LIST_HEAD(&adap->userspace_clients);
/* Set default timeout to 1 second if not already set */
if (adap->timeout == 0)
res = device_add(&adap->dev);
if (res) {
pr_err("adapter '%s': can't register device (%d)\n", adap->name, res);
+ put_device(&adap->dev);
goto out_list;
}
}
EXPORT_SYMBOL_GPL(i2c_add_numbered_adapter);
-static void i2c_do_del_adapter(struct i2c_driver *driver,
- struct i2c_adapter *adapter)
-{
- struct i2c_client *client, *_n;
-
- /* Remove the devices we created ourselves as the result of hardware
- * probing (using a driver's detect method) */
- list_for_each_entry_safe(client, _n, &driver->clients, detected) {
- if (client->adapter == adapter) {
- dev_dbg(&adapter->dev, "Removing %s at 0x%x\n",
- client->name, client->addr);
- list_del(&client->detected);
- i2c_unregister_device(client);
- }
- }
-}
-
static int __unregister_client(struct device *dev, void *dummy)
{
struct i2c_client *client = i2c_verify_client(dev);
return 0;
}
-static int __process_removed_adapter(struct device_driver *d, void *data)
-{
- i2c_do_del_adapter(to_i2c_driver(d), data);
- return 0;
-}
-
/**
* i2c_del_adapter - unregister I2C adapter
* @adap: the adapter being unregistered
void i2c_del_adapter(struct i2c_adapter *adap)
{
struct i2c_adapter *found;
- struct i2c_client *client, *next;
/* First make sure that this adapter was ever added */
mutex_lock(&core_lock);
}
i2c_acpi_remove_space_handler(adap);
- /* Tell drivers about this removal */
- mutex_lock(&core_lock);
- bus_for_each_drv(&i2c_bus_type, NULL, adap,
- __process_removed_adapter);
- mutex_unlock(&core_lock);
-
- /* Remove devices instantiated from sysfs */
- mutex_lock_nested(&adap->userspace_clients_lock,
- i2c_adapter_depth(adap));
- list_for_each_entry_safe(client, next, &adap->userspace_clients,
- detected) {
- dev_dbg(&adap->dev, "Removing %s at 0x%x\n", client->name,
- client->addr);
- list_del(&client->detected);
- i2c_unregister_device(client);
- }
- mutex_unlock(&adap->userspace_clients_lock);
/* Detach any active clients. This can't fail, thus we do not
* check the returned value. This is a two-pass process, because
* we can't remove the dummy devices during the first pass: they
* could have been instantiated by real devices wishing to clean
* them up properly, so we give them a chance to do that first. */
+ mutex_lock(&core_lock);
device_for_each_child(&adap->dev, NULL, __unregister_client);
device_for_each_child(&adap->dev, NULL, __unregister_dummy);
+ mutex_unlock(&core_lock);
/* device name is gone after device_unregister */
dev_dbg(&adap->dev, "adapter [%s] unregistered\n", adap->name);
/* add the driver to the list of i2c drivers in the driver core */
driver->driver.owner = owner;
driver->driver.bus = &i2c_bus_type;
- INIT_LIST_HEAD(&driver->clients);
/* When registration returns, the driver core
* will have called probe() for all matching-but-unbound devices.
}
EXPORT_SYMBOL(i2c_register_driver);
-static int __process_removed_driver(struct device *dev, void *data)
+static int __i2c_unregister_detected_client(struct device *dev, void *argp)
{
- if (dev->type == &i2c_adapter_type)
- i2c_do_del_adapter(data, to_i2c_adapter(dev));
+ struct i2c_client *client = i2c_verify_client(dev);
+
+ if (client && client->flags & I2C_CLIENT_AUTO)
+ i2c_unregister_device(client);
+
return 0;
}
*/
void i2c_del_driver(struct i2c_driver *driver)
{
- i2c_for_each_dev(driver, __process_removed_driver);
+ mutex_lock(&core_lock);
+ /* Satisfy __must_check, function can't fail */
+ if (driver_for_each_device(&driver->driver, NULL, NULL,
+ __i2c_unregister_detected_client)) {
+ }
+ mutex_unlock(&core_lock);
driver_unregister(&driver->driver);
pr_debug("driver [%s] unregistered\n", driver->driver.name);
/* Finally call the custom detection function */
memset(&info, 0, sizeof(struct i2c_board_info));
info.addr = addr;
+ info.flags = I2C_CLIENT_AUTO;
err = driver->detect(temp_client, &info);
if (err) {
/* -ENODEV is returned if the detection fails. We catch it
dev_dbg(&adapter->dev, "Creating %s at 0x%02x\n",
info.type, info.addr);
client = i2c_new_client_device(adapter, &info);
- if (!IS_ERR(client))
- list_add_tail(&client->detected, &driver->clients);
- else
+ if (IS_ERR(client))
dev_err(&adapter->dev, "Failed creating %s at 0x%02x\n",
info.type, info.addr);
}
static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver)
{
const unsigned short *address_list;
- struct i2c_client *temp_client;
+ struct i2c_client temp_client;
int i, err = 0;
address_list = driver->address_list;
return 0;
/* Set up a temporary client to help detect callback */
- temp_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
- if (!temp_client)
- return -ENOMEM;
- temp_client->adapter = adapter;
+ memset(&temp_client, 0, sizeof(temp_client));
+ temp_client.adapter = adapter;
for (i = 0; address_list[i] != I2C_CLIENT_END; i += 1) {
dev_dbg(&adapter->dev,
"found normal entry for adapter %d, addr 0x%02x\n",
i2c_adapter_id(adapter), address_list[i]);
- temp_client->addr = address_list[i];
- err = i2c_detect_address(temp_client, driver);
+ temp_client.addr = address_list[i];
+ err = i2c_detect_address(&temp_client, driver);
if (unlikely(err))
break;
}
- kfree(temp_client);
return err;
}
/*
* CZ.NIC's Turris Omnia LEDs driver
*
*/
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
+#include <linux/turris-omnia-mcu-interface.h>
#define OMNIA_BOARD_LEDS 12
#define OMNIA_LED_NUM_CHANNELS 3
-/* MCU controller commands at I2C address 0x2a */
-#define OMNIA_MCU_I2C_ADDR 0x2a
-
-#define CMD_GET_STATUS_WORD 0x01
-#define STS_FEATURES_SUPPORTED BIT(2)
-
-#define CMD_GET_FEATURES 0x10
-#define FEAT_LED_GAMMA_CORRECTION BIT(5)
-
-/* LED controller commands at I2C address 0x2b */
-#define CMD_LED_MODE 0x03
-#define CMD_LED_MODE_LED(l) ((l) & 0x0f)
-#define CMD_LED_MODE_USER 0x10
-
-#define CMD_LED_STATE 0x04
-#define CMD_LED_STATE_LED(l) ((l) & 0x0f)
-#define CMD_LED_STATE_ON 0x10
-
-#define CMD_LED_COLOR 0x05
-#define CMD_LED_SET_BRIGHTNESS 0x07
-#define CMD_LED_GET_BRIGHTNESS 0x08
-
-#define CMD_SET_GAMMA_CORRECTION 0x30
-#define CMD_GET_GAMMA_CORRECTION 0x31
-
+/* MCU controller I2C address 0x2a, needed for detecting MCU features */
+#define OMNIA_MCU_I2C_ADDR 0x2a
+
+/**
+ * struct omnia_led - per-LED part of driver private data structure
+ * @mc_cdev: multi-color LED class device
+ * @subled_info: per-channel information
+ * @cached_channels: cached values of per-channel brightness that was sent to the MCU
+ * @on: whether the LED was set on
+ * @hwtrig: whether the LED blinking was offloaded to the MCU
+ * @reg: LED identifier to the MCU
+ */
struct omnia_led {
struct led_classdev_mc mc_cdev;
struct mc_subled subled_info[OMNIA_LED_NUM_CHANNELS];
#define to_omnia_led(l) container_of(l, struct omnia_led, mc_cdev)
+/**
+ * struct omnia_leds - driver private data structure
+ * @client: I2C client device
+ * @lock: mutex to protect cached state
+ * @has_gamma_correction: whether the MCU firmware supports gamma correction
+ * @brightness_knode: kernel node of the "brightness" device sysfs attribute (this is the
+ * driver specific global brightness, not the LED classdev brightness)
+ * @leds: flexible array of per-LED data
+ */
struct omnia_leds {
struct i2c_client *client;
struct mutex lock;
bool has_gamma_correction;
+ struct kernfs_node *brightness_knode;
struct omnia_led leds[];
};
-static int omnia_cmd_write_u8(const struct i2c_client *client, u8 cmd, u8 val)
-{
- u8 buf[2] = { cmd, val };
- int ret;
-
- ret = i2c_master_send(client, buf, sizeof(buf));
-
- return ret < 0 ? ret : 0;
-}
-
-static int omnia_cmd_read_raw(struct i2c_adapter *adapter, u8 addr, u8 cmd,
- void *reply, size_t len)
-{
- struct i2c_msg msgs[2];
- int ret;
-
- msgs[0].addr = addr;
- msgs[0].flags = 0;
- msgs[0].len = 1;
- msgs[0].buf = &cmd;
- msgs[1].addr = addr;
- msgs[1].flags = I2C_M_RD;
- msgs[1].len = len;
- msgs[1].buf = reply;
-
- ret = i2c_transfer(adapter, msgs, ARRAY_SIZE(msgs));
- if (likely(ret == ARRAY_SIZE(msgs)))
- return 0;
- else if (ret < 0)
- return ret;
- else
- return -EIO;
-}
-
-static int omnia_cmd_read_u8(const struct i2c_client *client, u8 cmd)
+static int omnia_cmd_set_color(const struct i2c_client *client, u8 led, u8 r, u8 g, u8 b)
{
- u8 reply;
- int err;
+ u8 buf[5] = { OMNIA_CMD_LED_COLOR, led, r, g, b };
- err = omnia_cmd_read_raw(client->adapter, client->addr, cmd, &reply, 1);
- if (err)
- return err;
-
- return reply;
+ return omnia_cmd_write(client, buf, sizeof(buf));
}
static int omnia_led_send_color_cmd(const struct i2c_client *client,
struct omnia_led *led)
{
- char cmd[5];
int ret;
- cmd[0] = CMD_LED_COLOR;
- cmd[1] = led->reg;
- cmd[2] = led->subled_info[0].brightness;
- cmd[3] = led->subled_info[1].brightness;
- cmd[4] = led->subled_info[2].brightness;
-
/* Send the color change command */
- ret = i2c_master_send(client, cmd, 5);
+ ret = omnia_cmd_set_color(client, led->reg, led->subled_info[0].brightness,
+ led->subled_info[1].brightness, led->subled_info[2].brightness);
if (ret < 0)
return ret;
* is not being blinked by HW.
*/
if (!err && !led->hwtrig && !brightness != !led->on) {
- u8 state = CMD_LED_STATE_LED(led->reg);
+ u8 state = OMNIA_CMD_LED_STATE_LED(led->reg);
if (brightness)
- state |= CMD_LED_STATE_ON;
+ state |= OMNIA_CMD_LED_STATE_ON;
- err = omnia_cmd_write_u8(leds->client, CMD_LED_STATE, state);
+ err = omnia_cmd_write_u8(leds->client, OMNIA_CMD_LED_STATE, state);
if (!err)
led->on = !!brightness;
}
if (!err) {
/* Put the LED into MCU controlled mode */
- err = omnia_cmd_write_u8(leds->client, CMD_LED_MODE,
- CMD_LED_MODE_LED(led->reg));
+ err = omnia_cmd_write_u8(leds->client, OMNIA_CMD_LED_MODE,
+ OMNIA_CMD_LED_MODE_LED(led->reg));
if (!err)
led->hwtrig = true;
}
led->hwtrig = false;
/* Put the LED into software mode */
- err = omnia_cmd_write_u8(leds->client, CMD_LED_MODE,
- CMD_LED_MODE_LED(led->reg) |
- CMD_LED_MODE_USER);
+ err = omnia_cmd_write_u8(leds->client, OMNIA_CMD_LED_MODE,
+ OMNIA_CMD_LED_MODE_LED(led->reg) | OMNIA_CMD_LED_MODE_USER);
mutex_unlock(&leds->lock);
*/
cdev->default_trigger = omnia_hw_trigger.name;
- /* put the LED into software mode */
- ret = omnia_cmd_write_u8(client, CMD_LED_MODE,
- CMD_LED_MODE_LED(led->reg) |
- CMD_LED_MODE_USER);
- if (ret) {
- dev_err(dev, "Cannot set LED %pOF to software mode: %i\n", np,
- ret);
- return ret;
- }
+ /* Put the LED into software mode */
+ ret = omnia_cmd_write_u8(client, OMNIA_CMD_LED_MODE, OMNIA_CMD_LED_MODE_LED(led->reg) |
+ OMNIA_CMD_LED_MODE_USER);
+ if (ret)
+ return dev_err_probe(dev, ret, "Cannot set LED %pOF to software mode\n", np);
- /* disable the LED */
- ret = omnia_cmd_write_u8(client, CMD_LED_STATE,
- CMD_LED_STATE_LED(led->reg));
- if (ret) {
- dev_err(dev, "Cannot set LED %pOF brightness: %i\n", np, ret);
- return ret;
- }
+ /* Disable the LED */
+ ret = omnia_cmd_write_u8(client, OMNIA_CMD_LED_STATE, OMNIA_CMD_LED_STATE_LED(led->reg));
+ if (ret)
+ return dev_err_probe(dev, ret, "Cannot set LED %pOF brightness\n", np);
/* Set initial color and cache it */
ret = omnia_led_send_color_cmd(client, led);
- if (ret < 0) {
- dev_err(dev, "Cannot set LED %pOF initial color: %i\n", np,
- ret);
- return ret;
- }
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Cannot set LED %pOF initial color\n", np);
ret = devm_led_classdev_multicolor_register_ext(dev, &led->mc_cdev,
&init_data);
- if (ret < 0) {
- dev_err(dev, "Cannot register LED %pOF: %i\n", np, ret);
- return ret;
- }
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Cannot register LED %pOF\n", np);
return 1;
}
char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
- int ret;
-
- ret = omnia_cmd_read_u8(client, CMD_LED_GET_BRIGHTNESS);
+ u8 reply;
+ int err;
- if (ret < 0)
- return ret;
+ err = omnia_cmd_read_u8(client, OMNIA_CMD_GET_BRIGHTNESS, &reply);
+ if (err < 0)
+ return err;
- return sysfs_emit(buf, "%d\n", ret);
+ return sysfs_emit(buf, "%d\n", reply);
}
static ssize_t brightness_store(struct device *dev, struct device_attribute *a,
if (brightness > 100)
return -EINVAL;
- err = omnia_cmd_write_u8(client, CMD_LED_SET_BRIGHTNESS, brightness);
+ err = omnia_cmd_write_u8(client, OMNIA_CMD_SET_BRIGHTNESS, brightness);
return err ?: count;
}
{
struct i2c_client *client = to_i2c_client(dev);
struct omnia_leds *leds = i2c_get_clientdata(client);
- int ret;
+ u8 reply = 0;
+ int err;
if (leds->has_gamma_correction) {
- ret = omnia_cmd_read_u8(client, CMD_GET_GAMMA_CORRECTION);
- if (ret < 0)
- return ret;
- } else {
- ret = 0;
+ err = omnia_cmd_read_u8(client, OMNIA_CMD_GET_GAMMA_CORRECTION, &reply);
+ if (err < 0)
+ return err;
}
- return sysfs_emit(buf, "%d\n", !!ret);
+ return sysfs_emit(buf, "%d\n", !!reply);
}
static ssize_t gamma_correction_store(struct device *dev,
if (kstrtobool(buf, &val) < 0)
return -EINVAL;
- err = omnia_cmd_write_u8(client, CMD_SET_GAMMA_CORRECTION, val);
+ err = omnia_cmd_write_u8(client, OMNIA_CMD_SET_GAMMA_CORRECTION, val);
return err ?: count;
}
};
ATTRIBUTE_GROUPS(omnia_led_controller);
-static int omnia_mcu_get_features(const struct i2c_client *client)
+static irqreturn_t omnia_brightness_changed_threaded_fn(int irq, void *data)
+{
+ struct omnia_leds *leds = data;
+
+ if (unlikely(!leds->brightness_knode)) {
+ /*
+ * Note that sysfs_get_dirent() may sleep. This is okay, because we are in threaded
+ * context.
+ */
+ leds->brightness_knode = sysfs_get_dirent(leds->client->dev.kobj.sd, "brightness");
+ if (!leds->brightness_knode)
+ return IRQ_NONE;
+ }
+
+ sysfs_notify_dirent(leds->brightness_knode);
+
+ return IRQ_HANDLED;
+}
+
+static void omnia_brightness_knode_put(void *data)
+{
+ struct omnia_leds *leds = data;
+
+ if (leds->brightness_knode)
+ sysfs_put(leds->brightness_knode);
+}
+
+static int omnia_request_brightness_irq(struct omnia_leds *leds)
+{
+ struct device *dev = &leds->client->dev;
+ int ret;
+
+ if (!leds->client->irq) {
+ dev_info(dev,
+ "Brightness change interrupt supported by MCU firmware but not described in device-tree\n");
+
+ return 0;
+ }
+
+ /*
+ * Registering the brightness_knode destructor before requesting the IRQ ensures that on
+ * removal the brightness_knode sysfs node is put only after the IRQ is freed.
+ * This is needed because the interrupt handler uses the knode.
+ */
+ ret = devm_add_action(dev, omnia_brightness_knode_put, leds);
+ if (ret < 0)
+ return ret;
+
+ return devm_request_threaded_irq(dev, leds->client->irq, NULL,
+ omnia_brightness_changed_threaded_fn, IRQF_ONESHOT,
+ "leds-turris-omnia", leds);
+}
+
+static int omnia_mcu_get_features(const struct i2c_client *mcu_client)
{
u16 reply;
int err;
- err = omnia_cmd_read_raw(client->adapter, OMNIA_MCU_I2C_ADDR,
- CMD_GET_STATUS_WORD, &reply, sizeof(reply));
+ err = omnia_cmd_read_u16(mcu_client, OMNIA_CMD_GET_STATUS_WORD, &reply);
if (err)
return err;
- /* Check whether MCU firmware supports the CMD_GET_FEAUTRES command */
- if (!(le16_to_cpu(reply) & STS_FEATURES_SUPPORTED))
+ /* Check whether MCU firmware supports the OMNIA_CMD_GET_FEAUTRES command */
+ if (!(reply & OMNIA_STS_FEATURES_SUPPORTED))
return 0;
- err = omnia_cmd_read_raw(client->adapter, OMNIA_MCU_I2C_ADDR,
- CMD_GET_FEATURES, &reply, sizeof(reply));
+ err = omnia_cmd_read_u16(mcu_client, OMNIA_CMD_GET_FEATURES, &reply);
if (err)
return err;
- return le16_to_cpu(reply);
+ return reply;
+}
+
- static int omnia_match_mcu_client(struct device *dev, void *data)
++static int omnia_match_mcu_client(struct device *dev, const void *data)
+{
+ struct i2c_client *client;
+
+ client = i2c_verify_client(dev);
+ if (!client)
+ return 0;
+
+ return client->addr == OMNIA_MCU_I2C_ADDR;
+}
+
+static int omnia_find_mcu_and_get_features(struct device *dev)
+{
+ struct device *mcu_dev;
+ int ret;
+
+ mcu_dev = device_find_child(dev->parent, NULL, omnia_match_mcu_client);
+ if (!mcu_dev)
+ return -ENODEV;
+
+ ret = omnia_mcu_get_features(i2c_verify_client(mcu_dev));
+
+ put_device(mcu_dev);
+
+ return ret;
}
static int omnia_leds_probe(struct i2c_client *client)
int ret, count;
count = of_get_available_child_count(np);
- if (!count) {
- dev_err(dev, "LEDs are not defined in device tree!\n");
- return -ENODEV;
- } else if (count > OMNIA_BOARD_LEDS) {
- dev_err(dev, "Too many LEDs defined in device tree!\n");
- return -EINVAL;
- }
+ if (count == 0)
+ return dev_err_probe(dev, -ENODEV, "LEDs are not defined in device tree!\n");
+ if (count > OMNIA_BOARD_LEDS)
+ return dev_err_probe(dev, -EINVAL, "Too many LEDs defined in device tree!\n");
leds = devm_kzalloc(dev, struct_size(leds, leds, count), GFP_KERNEL);
if (!leds)
leds->client = client;
i2c_set_clientdata(client, leds);
- ret = omnia_mcu_get_features(client);
- if (ret < 0) {
- dev_err(dev, "Cannot determine MCU supported features: %d\n",
- ret);
- return ret;
- }
+ ret = omnia_find_mcu_and_get_features(dev);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Cannot determine MCU supported features\n");
- leds->has_gamma_correction = ret & FEAT_LED_GAMMA_CORRECTION;
- if (!leds->has_gamma_correction) {
- dev_info(dev,
- "Your board's MCU firmware does not support the LED gamma correction feature.\n");
- dev_info(dev,
- "Consider upgrading MCU firmware with the omnia-mcutool utility.\n");
+ leds->has_gamma_correction = ret & OMNIA_FEAT_LED_GAMMA_CORRECTION;
+
+ if (ret & OMNIA_FEAT_BRIGHTNESS_INT) {
+ ret = omnia_request_brightness_irq(leds);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Cannot request brightness IRQ\n");
}
mutex_init(&leds->lock);
ret = devm_led_trigger_register(dev, &omnia_hw_trigger);
- if (ret < 0) {
- dev_err(dev, "Cannot register private LED trigger: %d\n", ret);
- return ret;
- }
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Cannot register private LED trigger\n");
led = &leds->leds[0];
for_each_available_child_of_node_scoped(np, child) {
static void omnia_leds_remove(struct i2c_client *client)
{
- u8 buf[5];
-
- /* put all LEDs into default (HW triggered) mode */
- omnia_cmd_write_u8(client, CMD_LED_MODE,
- CMD_LED_MODE_LED(OMNIA_BOARD_LEDS));
-
- /* set all LEDs color to [255, 255, 255] */
- buf[0] = CMD_LED_COLOR;
- buf[1] = OMNIA_BOARD_LEDS;
- buf[2] = 255;
- buf[3] = 255;
- buf[4] = 255;
+ /* Put all LEDs into default (HW triggered) mode */
+ omnia_cmd_write_u8(client, OMNIA_CMD_LED_MODE, OMNIA_CMD_LED_MODE_LED(OMNIA_BOARD_LEDS));
- i2c_master_send(client, buf, 5);
+ /* Set all LEDs color to [255, 255, 255] */
+ omnia_cmd_set_color(client, OMNIA_BOARD_LEDS, 255, 255, 255);
}
static const struct of_device_id of_omnia_leds_match[] = {
#include "mgb4_trigger.h"
#include "mgb4_core.h"
-#define MGB4_USER_IRQS 16
+#define MGB4_USER_IRQS 16
+#define MGB4_MGB4_BAR_ID 0
+#define MGB4_XDMA_BAR_ID 1
#define DIGITEQ_VID 0x1ed8
#define T100_DID 0x0101
};
#endif
- static int match_i2c_adap(struct device *dev, void *data)
+ static int match_i2c_adap(struct device *dev, const void *data)
{
return i2c_verify_adapter(dev) ? 1 : 0;
}
return dev ? to_i2c_adapter(dev) : NULL;
}
- static int match_spi_adap(struct device *dev, void *data)
+ static int match_spi_adap(struct device *dev, const void *data)
{
return to_spi_device(dev) ? 1 : 0;
}
MODULE_PARM_DESC(phyaddr, "Physical device address");
#define STMMAC_TX_THRESH(x) ((x)->dma_conf.dma_tx_size / 4)
-#define STMMAC_RX_THRESH(x) ((x)->dma_conf.dma_rx_size / 4)
/* Limit to make sure XDP TX and slow path can coexist */
#define STMMAC_XSK_TX_BUDGET_MAX 256
module_param(buf_sz, int, 0644);
MODULE_PARM_DESC(buf_sz, "DMA buffer size");
-#define STMMAC_RX_COPYBREAK 256
-
static const u32 default_msg_level = (NETIF_MSG_DRV | NETIF_MSG_PROBE |
NETIF_MSG_LINK | NETIF_MSG_IFUP |
NETIF_MSG_IFDOWN | NETIF_MSG_TIMER);
#define STMMAC_DEFAULT_LPI_TIMER 1000
-static int eee_timer = STMMAC_DEFAULT_LPI_TIMER;
-module_param(eee_timer, int, 0644);
+static unsigned int eee_timer = STMMAC_DEFAULT_LPI_TIMER;
+module_param(eee_timer, uint, 0644);
MODULE_PARM_DESC(eee_timer, "LPI tx expiration time in msec");
#define STMMAC_LPI_T(x) (jiffies + usecs_to_jiffies(x))
flow_ctrl = FLOW_OFF;
if (unlikely((pause < 0) || (pause > 0xffff)))
pause = PAUSE_TIME;
- if (eee_timer < 0)
- eee_timer = STMMAC_DEFAULT_LPI_TIMER;
}
static void __stmmac_disable_all_queues(struct stmmac_priv *priv)
*/
static void stmmac_clk_csr_set(struct stmmac_priv *priv)
{
- u32 clk_rate;
+ unsigned long clk_rate;
clk_rate = clk_get_rate(priv->plat->stmmac_clk);
priv->clk_csr = STMMAC_CSR_150_250M;
else if ((clk_rate >= CSR_F_250M) && (clk_rate <= CSR_F_300M))
priv->clk_csr = STMMAC_CSR_250_300M;
+ else if ((clk_rate >= CSR_F_300M) && (clk_rate < CSR_F_500M))
+ priv->clk_csr = STMMAC_CSR_300_500M;
+ else if ((clk_rate >= CSR_F_500M) && (clk_rate < CSR_F_800M))
+ priv->clk_csr = STMMAC_CSR_500_800M;
}
if (priv->plat->flags & STMMAC_FLAG_HAS_SUN8I) {
return dirty;
}
-static void stmmac_lpi_entry_timer_config(struct stmmac_priv *priv, bool en)
+static void stmmac_disable_hw_lpi_timer(struct stmmac_priv *priv)
{
- int tx_lpi_timer;
+ stmmac_set_eee_lpi_timer(priv, priv->hw, 0);
+}
- /* Clear/set the SW EEE timer flag based on LPI ET enablement */
- priv->eee_sw_timer_en = en ? 0 : 1;
- tx_lpi_timer = en ? priv->tx_lpi_timer : 0;
- stmmac_set_eee_lpi_timer(priv, priv->hw, tx_lpi_timer);
+static void stmmac_enable_hw_lpi_timer(struct stmmac_priv *priv)
+{
+ stmmac_set_eee_lpi_timer(priv, priv->hw, priv->tx_lpi_timer);
}
-/**
- * stmmac_enable_eee_mode - check and enter in LPI mode
- * @priv: driver private structure
- * Description: this function is to verify and enter in LPI mode in case of
- * EEE.
- */
-static int stmmac_enable_eee_mode(struct stmmac_priv *priv)
+static bool stmmac_eee_tx_busy(struct stmmac_priv *priv)
{
u32 tx_cnt = priv->plat->tx_queues_to_use;
u32 queue;
struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
if (tx_q->dirty_tx != tx_q->cur_tx)
- return -EBUSY; /* still unfinished work */
+ return true; /* still unfinished work */
+ }
+
+ return false;
+}
+
+static void stmmac_restart_sw_lpi_timer(struct stmmac_priv *priv)
+{
+ mod_timer(&priv->eee_ctrl_timer, STMMAC_LPI_T(priv->tx_lpi_timer));
+}
+
+/**
+ * stmmac_try_to_start_sw_lpi - check and enter in LPI mode
+ * @priv: driver private structure
+ * Description: this function is to verify and enter in LPI mode in case of
+ * EEE.
+ */
+static void stmmac_try_to_start_sw_lpi(struct stmmac_priv *priv)
+{
+ if (stmmac_eee_tx_busy(priv)) {
+ stmmac_restart_sw_lpi_timer(priv);
+ return;
}
/* Check and enter in LPI mode */
if (!priv->tx_path_in_lpi_mode)
stmmac_set_eee_mode(priv, priv->hw,
priv->plat->flags & STMMAC_FLAG_EN_TX_LPI_CLOCKGATING);
- return 0;
}
/**
- * stmmac_disable_eee_mode - disable and exit from LPI mode
+ * stmmac_stop_sw_lpi - stop transmitting LPI
* @priv: driver private structure
- * Description: this function is to exit and disable EEE in case of
- * LPI state is true. This is called by the xmit.
+ * Description: When using software-controlled LPI, stop transmitting LPI state.
*/
-void stmmac_disable_eee_mode(struct stmmac_priv *priv)
+static void stmmac_stop_sw_lpi(struct stmmac_priv *priv)
{
- if (!priv->eee_sw_timer_en) {
- stmmac_lpi_entry_timer_config(priv, 0);
- return;
- }
-
stmmac_reset_eee_mode(priv, priv->hw);
del_timer_sync(&priv->eee_ctrl_timer);
priv->tx_path_in_lpi_mode = false;
{
struct stmmac_priv *priv = from_timer(priv, t, eee_ctrl_timer);
- if (stmmac_enable_eee_mode(priv))
- mod_timer(&priv->eee_ctrl_timer, STMMAC_LPI_T(priv->tx_lpi_timer));
+ stmmac_try_to_start_sw_lpi(priv);
}
/**
* stmmac_eee_init - init EEE
* @priv: driver private structure
+ * @active: indicates whether EEE should be enabled.
* Description:
* if the GMAC supports the EEE (from the HW cap reg) and the phy device
* can also manage EEE, this function enable the LPI state and start related
* timer.
*/
-bool stmmac_eee_init(struct stmmac_priv *priv)
+static void stmmac_eee_init(struct stmmac_priv *priv, bool active)
{
- int eee_tw_timer = priv->eee_tw_timer;
+ priv->eee_active = active;
/* Check if MAC core supports the EEE feature. */
- if (!priv->dma_cap.eee)
- return false;
+ if (!priv->dma_cap.eee) {
+ priv->eee_enabled = false;
+ return;
+ }
mutex_lock(&priv->lock);
if (!priv->eee_active) {
if (priv->eee_enabled) {
netdev_dbg(priv->dev, "disable EEE\n");
- stmmac_lpi_entry_timer_config(priv, 0);
+ priv->eee_sw_timer_en = false;
+ stmmac_disable_hw_lpi_timer(priv);
del_timer_sync(&priv->eee_ctrl_timer);
- stmmac_set_eee_timer(priv, priv->hw, 0, eee_tw_timer);
+ stmmac_set_eee_timer(priv, priv->hw, 0,
+ STMMAC_DEFAULT_TWT_LS);
if (priv->hw->xpcs)
xpcs_config_eee(priv->hw->xpcs,
priv->plat->mult_fact_100ns,
false);
}
+ priv->eee_enabled = false;
mutex_unlock(&priv->lock);
- return false;
+ return;
}
if (priv->eee_active && !priv->eee_enabled) {
- timer_setup(&priv->eee_ctrl_timer, stmmac_eee_ctrl_timer, 0);
stmmac_set_eee_timer(priv, priv->hw, STMMAC_DEFAULT_LIT_LS,
- eee_tw_timer);
+ STMMAC_DEFAULT_TWT_LS);
if (priv->hw->xpcs)
xpcs_config_eee(priv->hw->xpcs,
priv->plat->mult_fact_100ns,
}
if (priv->plat->has_gmac4 && priv->tx_lpi_timer <= STMMAC_ET_MAX) {
+ /* Use hardware LPI mode */
del_timer_sync(&priv->eee_ctrl_timer);
priv->tx_path_in_lpi_mode = false;
- stmmac_lpi_entry_timer_config(priv, 1);
+ priv->eee_sw_timer_en = false;
+ stmmac_enable_hw_lpi_timer(priv);
} else {
- stmmac_lpi_entry_timer_config(priv, 0);
- mod_timer(&priv->eee_ctrl_timer,
- STMMAC_LPI_T(priv->tx_lpi_timer));
+ /* Use software LPI mode */
+ priv->eee_sw_timer_en = true;
+ stmmac_disable_hw_lpi_timer(priv);
+ stmmac_restart_sw_lpi_timer(priv);
}
+ priv->eee_enabled = true;
+
mutex_unlock(&priv->lock);
netdev_dbg(priv->dev, "Energy-Efficient Ethernet initialized\n");
- return true;
}
/* stmmac_get_tx_hwtstamp - get HW TX timestamps
struct stmmac_priv *priv = netdev_priv(to_net_dev(config->dev));
stmmac_mac_set(priv, priv->ioaddr, false);
- priv->eee_active = false;
- priv->tx_lpi_enabled = false;
- priv->eee_enabled = stmmac_eee_init(priv);
- stmmac_set_eee_pls(priv, priv->hw, false);
+ if (priv->dma_cap.eee)
+ stmmac_set_eee_pls(priv, priv->hw, false);
if (stmmac_fpe_supported(priv))
stmmac_fpe_link_state_handle(priv, false);
writel(ctrl, priv->ioaddr + MAC_CTRL_REG);
stmmac_mac_set(priv, priv->ioaddr, true);
- if (phy && priv->dma_cap.eee) {
- priv->eee_active =
- phy_init_eee(phy, !(priv->plat->flags &
- STMMAC_FLAG_RX_CLK_RUNS_IN_LPI)) >= 0;
- priv->eee_enabled = stmmac_eee_init(priv);
- priv->tx_lpi_enabled = priv->eee_enabled;
+ if (priv->dma_cap.eee)
stmmac_set_eee_pls(priv, priv->hw, true);
- }
if (stmmac_fpe_supported(priv))
stmmac_fpe_link_state_handle(priv, true);
stmmac_hwtstamp_correct_latency(priv, priv);
}
+static void stmmac_mac_disable_tx_lpi(struct phylink_config *config)
+{
+ struct stmmac_priv *priv = netdev_priv(to_net_dev(config->dev));
+
+ stmmac_eee_init(priv, false);
+}
+
+static int stmmac_mac_enable_tx_lpi(struct phylink_config *config, u32 timer,
+ bool tx_clk_stop)
+{
+ struct stmmac_priv *priv = netdev_priv(to_net_dev(config->dev));
+
+ priv->tx_lpi_timer = timer;
+ stmmac_eee_init(priv, true);
+
+ return 0;
+}
+
static const struct phylink_mac_ops stmmac_phylink_mac_ops = {
.mac_get_caps = stmmac_mac_get_caps,
.mac_select_pcs = stmmac_mac_select_pcs,
.mac_config = stmmac_mac_config,
.mac_link_down = stmmac_mac_link_down,
.mac_link_up = stmmac_mac_link_up,
+ .mac_disable_tx_lpi = stmmac_mac_disable_tx_lpi,
+ .mac_enable_tx_lpi = stmmac_mac_enable_tx_lpi,
};
/**
return -ENODEV;
}
- if (priv->dma_cap.eee)
- phy_support_eee(phydev);
-
ret = phylink_connect_phy(priv->phylink, phydev);
} else {
fwnode_handle_put(phy_fwnode);
ret = phylink_fwnode_phy_connect(priv->phylink, fwnode, 0);
}
+ if (ret == 0) {
+ struct ethtool_keee eee;
+
+ /* Configure phylib's copy of the LPI timer. Normally,
+ * phylink_config.lpi_timer_default would do this, but there is
+ * a chance that userspace could change the eee_timer setting
+ * via sysfs before the first open. Thus, preserve existing
+ * behaviour.
+ */
+ if (!phylink_ethtool_get_eee(priv->phylink, &eee)) {
+ eee.tx_lpi_timer = priv->tx_lpi_timer;
+ phylink_ethtool_set_eee(priv->phylink, &eee);
+ }
+ }
+
if (!priv->plat->pmt) {
struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL };
struct stmmac_mdio_bus_data *mdio_bus_data;
int mode = priv->plat->phy_interface;
struct fwnode_handle *fwnode;
+ struct phylink_pcs *pcs;
struct phylink *phylink;
priv->phylink_config.dev = &priv->dev->dev;
/* Stmmac always requires an RX clock for hardware initialization */
priv->phylink_config.mac_requires_rxc = true;
+ if (!(priv->plat->flags & STMMAC_FLAG_RX_CLK_RUNS_IN_LPI))
+ priv->phylink_config.eee_rx_clk_stop_enable = true;
+
mdio_bus_data = priv->plat->mdio_bus_data;
if (mdio_bus_data)
priv->phylink_config.default_an_inband =
/* If we have an xpcs, it defines which PHY interfaces are supported. */
if (priv->hw->xpcs)
- xpcs_get_interfaces(priv->hw->xpcs,
- priv->phylink_config.supported_interfaces);
+ pcs = xpcs_to_phylink_pcs(priv->hw->xpcs);
+ else
+ pcs = priv->hw->phylink_pcs;
+
+ if (pcs)
+ phy_interface_or(priv->phylink_config.supported_interfaces,
+ priv->phylink_config.supported_interfaces,
+ pcs->supported_interfaces);
+
+ if (priv->dma_cap.eee) {
+ /* Assume all supported interfaces also support LPI */
+ memcpy(priv->phylink_config.lpi_interfaces,
+ priv->phylink_config.supported_interfaces,
+ sizeof(priv->phylink_config.lpi_interfaces));
+
+ /* All full duplex speeds above 100Mbps are supported */
+ priv->phylink_config.lpi_capabilities = ~(MAC_1000FD - 1) |
+ MAC_100FD;
+ priv->phylink_config.lpi_timer_default = eee_timer * 1000;
+ priv->phylink_config.eee_enabled_default = true;
+ }
fwnode = priv->plat->port_node;
if (!fwnode)
stmmac_display_tx_rings(priv, dma_conf);
}
+static unsigned int stmmac_rx_offset(struct stmmac_priv *priv)
+{
+ if (stmmac_xdp_is_enabled(priv))
+ return XDP_PACKET_HEADROOM;
+
+ return NET_SKB_PAD;
+}
+
static int stmmac_set_bfsize(int mtu, int bufsize)
{
int ret = bufsize;
struct stmmac_channel *ch = &priv->channel[queue];
bool xdp_prog = stmmac_xdp_is_enabled(priv);
struct page_pool_params pp_params = { 0 };
- unsigned int num_pages;
+ unsigned int dma_buf_sz_pad, num_pages;
unsigned int napi_id;
int ret;
+ dma_buf_sz_pad = stmmac_rx_offset(priv) + dma_conf->dma_buf_sz +
+ SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
+ num_pages = DIV_ROUND_UP(dma_buf_sz_pad, PAGE_SIZE);
+
rx_q->queue_index = queue;
rx_q->priv_data = priv;
+ rx_q->napi_skb_frag_size = num_pages * PAGE_SIZE;
pp_params.flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV;
pp_params.pool_size = dma_conf->dma_rx_size;
- num_pages = DIV_ROUND_UP(dma_conf->dma_buf_sz, PAGE_SIZE);
- pp_params.order = ilog2(num_pages);
+ pp_params.order = order_base_2(num_pages);
pp_params.nid = dev_to_node(priv->device);
pp_params.dev = priv->device;
pp_params.dma_dir = xdp_prog ? DMA_BIDIRECTIONAL : DMA_FROM_DEVICE;
pp_params.offset = stmmac_rx_offset(priv);
- pp_params.max_len = STMMAC_MAX_RX_BUF_SIZE(num_pages);
+ pp_params.max_len = dma_conf->dma_buf_sz;
rx_q->page_pool = page_pool_create(&pp_params);
if (IS_ERR(rx_q->page_pool)) {
xmits = budget;
}
- if (priv->eee_enabled && !priv->tx_path_in_lpi_mode &&
- priv->eee_sw_timer_en) {
- if (stmmac_enable_eee_mode(priv))
- mod_timer(&priv->eee_ctrl_timer, STMMAC_LPI_T(priv->tx_lpi_timer));
- }
+ if (priv->eee_sw_timer_en && !priv->tx_path_in_lpi_mode)
+ stmmac_restart_sw_lpi_timer(priv);
/* We still have pending packets, let's call for a new scheduling */
if (tx_q->dirty_tx != tx_q->cur_tx)
else if (ptp_register)
stmmac_ptp_register(priv);
- priv->eee_tw_timer = STMMAC_DEFAULT_TWT_LS;
-
- /* Convert the timer from msec to usec */
- if (!priv->tx_lpi_timer)
- priv->tx_lpi_timer = eee_timer * 1000;
-
if (priv->use_riwt) {
u32 queue;
u32 chan;
int ret;
+ /* Initialise the tx lpi timer, converting from msec to usec */
+ if (!priv->tx_lpi_timer)
+ priv->tx_lpi_timer = eee_timer * 1000;
+
ret = pm_runtime_resume_and_get(priv->device);
if (ret < 0)
return ret;
}
}
- priv->rx_copybreak = STMMAC_RX_COPYBREAK;
-
buf_sz = dma_conf->dma_buf_sz;
for (int i = 0; i < MTL_MAX_TX_QUEUES; i++)
if (priv->dma_conf.tx_queue[i].tbs & STMMAC_TBS_EN)
/* Free the IRQ lines */
stmmac_free_irq(dev, REQ_IRQ_ERR_ALL, 0);
- if (priv->eee_enabled) {
- priv->tx_path_in_lpi_mode = false;
- del_timer_sync(&priv->eee_ctrl_timer);
- }
-
/* Stop TX/RX DMA and clear the descriptors */
stmmac_stop_all_dma(priv);
desc = &tx_q->dma_tx[tx_q->cur_tx];
curr_addr = des + (total_len - tmp_len);
- if (priv->dma_cap.addr64 <= 32)
- desc->des0 = cpu_to_le32(curr_addr);
- else
- stmmac_set_desc_addr(priv, desc, curr_addr);
-
+ stmmac_set_desc_addr(priv, desc, curr_addr);
buff_size = tmp_len >= TSO_MAX_BUFF_SIZE ?
TSO_MAX_BUFF_SIZE : tmp_len;
* First Descriptor
* --------
* | DES0 |---> buffer1 = L2/L3/L4 header
- * | DES1 |---> TCP Payload (can continue on next descr...)
- * | DES2 |---> buffer 1 and 2 len
+ * | DES1 |---> can be used as buffer2 for TCP Payload if the DMA AXI address
+ * | | width is 32-bit, but we never use it.
+ * | | Also can be used as the most-significant 8-bits or 16-bits of
+ * | | buffer1 address pointer if the DMA AXI address width is 40-bit
+ * | | or 48-bit, and we always use it.
+ * | DES2 |---> buffer1 len
* | DES3 |---> must set TSE, TCP hdr len-> [22:19]. TCP payload len [17:0]
* --------
+ * --------
+ * | DES0 |---> buffer1 = TCP Payload (can continue on next descr...)
+ * | DES1 |---> same as the First Descriptor
+ * | DES2 |---> buffer1 len
+ * | DES3 |
+ * --------
* |
* ...
* |
* --------
- * | DES0 | --| Split TCP Payload on Buffers 1 and 2
- * | DES1 | --|
- * | DES2 | --> buffer 1 and 2 len
+ * | DES0 |---> buffer1 = Split TCP Payload
+ * | DES1 |---> same as the First Descriptor
+ * | DES2 |---> buffer1 len
* | DES3 |
* --------
*
{
struct dma_desc *desc, *first, *mss_desc = NULL;
struct stmmac_priv *priv = netdev_priv(dev);
- int tmp_pay_len = 0, first_tx, nfrags;
unsigned int first_entry, tx_packets;
struct stmmac_txq_stats *txq_stats;
struct stmmac_tx_queue *tx_q;
u32 pay_len, mss, queue;
- dma_addr_t tso_des, des;
+ int i, first_tx, nfrags;
u8 proto_hdr_len, hdr;
+ dma_addr_t des;
bool set_ic;
- int i;
/* Always insert VLAN tag to SKB payload for TSO frames.
*
if (dma_mapping_error(priv->device, des))
goto dma_map_err;
- if (priv->dma_cap.addr64 <= 32) {
- first->des0 = cpu_to_le32(des);
-
- /* Fill start of payload in buff2 of first descriptor */
- if (pay_len)
- first->des1 = cpu_to_le32(des + proto_hdr_len);
-
- /* If needed take extra descriptors to fill the remaining payload */
- tmp_pay_len = pay_len - TSO_MAX_BUFF_SIZE;
- tso_des = des;
- } else {
- stmmac_set_desc_addr(priv, first, des);
- tmp_pay_len = pay_len;
- tso_des = des + proto_hdr_len;
- pay_len = 0;
- }
-
- stmmac_tso_allocator(priv, tso_des, tmp_pay_len, (nfrags == 0), queue);
+ stmmac_set_desc_addr(priv, first, des);
+ stmmac_tso_allocator(priv, des + proto_hdr_len, pay_len,
+ (nfrags == 0), queue);
/* In case two or more DMA transmit descriptors are allocated for this
* non-paged SKB data, the DMA buffer address should be saved to
}
/* Complete the first descriptor before granting the DMA */
- stmmac_prepare_tso_tx_desc(priv, first, 1,
- proto_hdr_len,
- pay_len,
- 1, tx_q->tx_skbuff_dma[first_entry].last_segment,
- hdr / 4, (skb->len - proto_hdr_len));
+ stmmac_prepare_tso_tx_desc(priv, first, 1, proto_hdr_len, 0, 1,
+ tx_q->tx_skbuff_dma[first_entry].last_segment,
+ hdr / 4, (skb->len - proto_hdr_len));
/* If context desc is used to change MSS */
if (mss_desc) {
first_tx = tx_q->cur_tx;
if (priv->tx_path_in_lpi_mode && priv->eee_sw_timer_en)
- stmmac_disable_eee_mode(priv);
+ stmmac_stop_sw_lpi(priv);
/* Manage oversized TCP frames for GMAC4 device */
if (skb_is_gso(skb) && priv->tso) {
if (priv->extend_desc)
stmmac_rx_extended_status(priv, &priv->xstats, rx_q->dma_erx + entry);
if (unlikely(status == discard_frame)) {
- page_pool_recycle_direct(rx_q->page_pool, buf->page);
+ page_pool_put_page(rx_q->page_pool, buf->page, 0, true);
buf->page = NULL;
error = 1;
if (!priv->hwts_rx_en)
/* Buffer is good. Go on. */
- prefetch(page_address(buf->page) + buf->page_offset);
- if (buf->sec_page)
- prefetch(page_address(buf->sec_page));
-
buf1_len = stmmac_rx_buf1_len(priv, p, status, len);
len += buf1_len;
buf2_len = stmmac_rx_buf2_len(priv, p, status, len);
dma_sync_single_for_cpu(priv->device, buf->addr,
buf1_len, dma_dir);
+ net_prefetch(page_address(buf->page) +
+ buf->page_offset);
xdp_init_buff(&ctx.xdp, buf_sz, &rx_q->xdp_rxq);
xdp_prepare_buff(&ctx.xdp, page_address(buf->page),
}
if (!skb) {
+ unsigned int head_pad_len;
+
/* XDP program may expand or reduce tail */
buf1_len = ctx.xdp.data_end - ctx.xdp.data;
- skb = napi_alloc_skb(&ch->rx_napi, buf1_len);
+ skb = napi_build_skb(page_address(buf->page),
+ rx_q->napi_skb_frag_size);
if (!skb) {
+ page_pool_recycle_direct(rx_q->page_pool,
+ buf->page);
rx_dropped++;
count++;
goto drain_data;
}
/* XDP program may adjust header */
- skb_copy_to_linear_data(skb, ctx.xdp.data, buf1_len);
+ head_pad_len = ctx.xdp.data - ctx.xdp.data_hard_start;
+ skb_reserve(skb, head_pad_len);
skb_put(skb, buf1_len);
-
- /* Data payload copied into SKB, page ready for recycle */
- page_pool_recycle_direct(rx_q->page_pool, buf->page);
+ skb_mark_for_recycle(skb);
buf->page = NULL;
} else if (buf1_len) {
dma_sync_single_for_cpu(priv->device, buf->addr,
skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags,
buf->page, buf->page_offset, buf1_len,
priv->dma_conf.dma_buf_sz);
-
- /* Data payload appended into SKB */
- skb_mark_for_recycle(skb);
buf->page = NULL;
}
skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags,
buf->sec_page, 0, buf2_len,
priv->dma_conf.dma_buf_sz);
-
- /* Data payload appended into SKB */
- skb_mark_for_recycle(skb);
buf->sec_page = NULL;
}
switch (event) {
case NETDEV_CHANGENAME:
- if (priv->dbgfs_dir)
- priv->dbgfs_dir = debugfs_rename(stmmac_fs_dir,
- priv->dbgfs_dir,
- stmmac_fs_dir,
- dev->name);
+ debugfs_change_name(priv->dbgfs_dir, "%s", dev->name);
break;
}
done:
INIT_WORK(&priv->service_task, stmmac_service_task);
+ timer_setup(&priv->eee_ctrl_timer, stmmac_eee_ctrl_timer, 0);
+
/* Override with kernel parameters if supplied XXX CRS XXX
* this needs to have multiple instances
*/
for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++)
hrtimer_cancel(&priv->dma_conf.tx_queue[chan].txtimer);
- if (priv->eee_enabled) {
+ if (priv->eee_sw_timer_en) {
priv->tx_path_in_lpi_mode = false;
del_timer_sync(&priv->eee_ctrl_timer);
}
reg = <0x100>;
};
};
+
+ test-device@2 {
+ compatible = "test,rust-device";
+ reg = <0x2>;
+ };
};
+
+ platform-tests-2 {
+ // No #address-cells or #size-cells
+ node {
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ test-device@100 {
+ compatible = "test-sub-device";
+ reg = <0x100 1>;
+ };
+ };
+ };
};
};
BUG_ON(WFHWSIZE < ops->sizeof_wfhw);
+ if (!pwmchip_supports_waveform(chip))
+ return -EOPNOTSUPP;
+
if (!pwm_wf_valid(wf))
return -EINVAL;
BUG_ON(WFHWSIZE < ops->sizeof_wfhw);
+ if (!pwmchip_supports_waveform(chip) || !ops->read_waveform)
+ return -EOPNOTSUPP;
+
guard(pwmchip)(chip);
if (!chip->operational)
BUG_ON(WFHWSIZE < ops->sizeof_wfhw);
+ if (!pwmchip_supports_waveform(chip))
+ return -EOPNOTSUPP;
+
if (!pwm_wf_valid(wf))
return -EINVAL;
state->usage_power == pwm->state.usage_power)
return 0;
- if (ops->write_waveform) {
+ if (pwmchip_supports_waveform(chip)) {
struct pwm_waveform wf;
char wfhw[WFHWSIZE];
if (!chip->operational)
return -ENODEV;
- if (ops->read_waveform) {
+ if (pwmchip_supports_waveform(chip) && ops->read_waveform) {
char wfhw[WFHWSIZE];
struct pwm_waveform wf;
return 0;
}
- static int pwm_unexport_match(struct device *pwm_dev, void *data)
+ static int pwm_unexport_match(struct device *pwm_dev, const void *data)
{
return pwm_from_dev(pwm_dev) == data;
}
static int qla4xxx_eh_device_reset(struct scsi_cmnd *cmd);
static int qla4xxx_eh_target_reset(struct scsi_cmnd *cmd);
static int qla4xxx_eh_host_reset(struct scsi_cmnd *cmd);
-static int qla4xxx_slave_alloc(struct scsi_device *device);
+static int qla4xxx_sdev_init(struct scsi_device *device);
static umode_t qla4_attr_is_visible(int param_type, int param);
static int qla4xxx_host_reset(struct Scsi_Host *shost, int reset_type);
.eh_host_reset_handler = qla4xxx_eh_host_reset,
.eh_timed_out = qla4xxx_eh_cmd_timed_out,
- .slave_alloc = qla4xxx_slave_alloc,
+ .sdev_init = qla4xxx_sdev_init,
.change_queue_depth = scsi_change_queue_depth,
.this_id = -1,
* 1: if flashnode entry is non-persistent
* 0: if flashnode entry is persistent
**/
- static int qla4xxx_sysfs_ddb_is_non_persistent(struct device *dev, void *data)
+ static int qla4xxx_sysfs_ddb_is_non_persistent(struct device *dev,
+ const void *data)
{
struct iscsi_bus_flash_session *fnode_sess;
}
}
-static int qla4xxx_slave_alloc(struct scsi_device *sdev)
+static int qla4xxx_sdev_init(struct scsi_device *sdev)
{
struct iscsi_cls_session *cls_sess;
struct iscsi_session *sess;
.resume = qla4xxx_pci_resume,
};
-static struct pci_device_id qla4xxx_pci_tbl[] = {
+static const struct pci_device_id qla4xxx_pci_tbl[] = {
{
.vendor = PCI_VENDOR_ID_QLOGIC,
.device = PCI_DEVICE_ID_QLOGIC_ISP4010,
* 1 on success
* 0 on failure
*/
- static int iscsi_is_flashnode_conn_dev(struct device *dev, void *data)
+ static int iscsi_is_flashnode_conn_dev(struct device *dev, const void *data)
{
return dev->bus == &iscsi_flashnode_bus;
}
return 0;
}
- static int flashnode_match_index(struct device *dev, void *data)
+ static int flashnode_match_index(struct device *dev, const void *data)
{
struct iscsi_bus_flash_session *fnode_sess = NULL;
int ret = 0;
goto exit_match_index;
fnode_sess = iscsi_dev_to_flash_session(dev);
- ret = (fnode_sess->target_id == *((int *)data)) ? 1 : 0;
+ ret = (fnode_sess->target_id == *((const int *)data)) ? 1 : 0;
exit_match_index:
return ret;
* %NULL on failure
*/
struct device *
- iscsi_find_flashnode_sess(struct Scsi_Host *shost, void *data,
- int (*fn)(struct device *dev, void *data))
+ iscsi_find_flashnode_sess(struct Scsi_Host *shost, const void *data,
+ device_match_t fn)
{
return device_find_child(&shost->shost_gendev, data, fn);
}
}
EXPORT_SYMBOL_GPL(iscsi_add_session);
-/**
- * iscsi_create_session - create iscsi class session
- * @shost: scsi host
- * @transport: iscsi transport
- * @dd_size: private driver data size
- * @target_id: which target
- *
- * This can be called from a LLD or iscsi_transport.
- */
-struct iscsi_cls_session *
-iscsi_create_session(struct Scsi_Host *shost, struct iscsi_transport *transport,
- int dd_size, unsigned int target_id)
-{
- struct iscsi_cls_session *session;
-
- session = iscsi_alloc_session(shost, transport, dd_size);
- if (!session)
- return NULL;
-
- if (iscsi_add_session(session, target_id)) {
- iscsi_free_session(session);
- return NULL;
- }
- return session;
-}
-EXPORT_SYMBOL_GPL(iscsi_create_session);
-
static void iscsi_conn_release(struct device *dev)
{
struct iscsi_cls_conn *conn = iscsi_dev_to_conn(dev);
}
do {
/*
- * special case for GET_STATS:
+ * special case for GET_STATS, GET_CHAP and GET_HOST_STATS:
* on success - sending reply and stats from
* inside of if_recv_msg(),
* on error - fall through.
break;
if (ev->type == ISCSI_UEVENT_GET_CHAP && !err)
break;
+ if (ev->type == ISCSI_UEVENT_GET_HOST_STATS && !err)
+ break;
err = iscsi_if_send_reply(portid, nlh->nlmsg_type,
ev, sizeof(*ev));
if (err == -EAGAIN && --retries < 0) {
{
struct uart_state *state = container_of(port, struct uart_state, port);
struct uart_port *uport;
- int ret = -ENODEV;
/* Initialize structure in case we error out later to prevent any stack info leakage. */
*retinfo = (struct serial_struct){};
* Ensure the state we copy is consistent and no hardware changes
* occur as we go
*/
- mutex_lock(&port->mutex);
+ guard(mutex)(&port->mutex);
uport = uart_port_check(state);
if (!uport)
- goto out;
+ return -ENODEV;
retinfo->type = uport->type;
retinfo->line = uport->line;
retinfo->iomem_reg_shift = uport->regshift;
retinfo->iomem_base = (void *)(unsigned long)uport->mapbase;
- ret = 0;
-out:
- mutex_unlock(&port->mutex);
- return ret;
+ return 0;
}
static int uart_get_info_user(struct tty_struct *tty,
return uart_get_info(port, ss) < 0 ? -EIO : 0;
}
+static int uart_change_port(struct uart_port *uport,
+ const struct serial_struct *new_info,
+ unsigned long new_port)
+{
+ unsigned long old_iobase, old_mapbase;
+ unsigned int old_type, old_iotype, old_hub6, old_shift;
+ int retval;
+
+ old_iobase = uport->iobase;
+ old_mapbase = uport->mapbase;
+ old_type = uport->type;
+ old_hub6 = uport->hub6;
+ old_iotype = uport->iotype;
+ old_shift = uport->regshift;
+
+ if (old_type != PORT_UNKNOWN && uport->ops->release_port)
+ uport->ops->release_port(uport);
+
+ uport->iobase = new_port;
+ uport->type = new_info->type;
+ uport->hub6 = new_info->hub6;
+ uport->iotype = new_info->io_type;
+ uport->regshift = new_info->iomem_reg_shift;
+ uport->mapbase = (unsigned long)new_info->iomem_base;
+
+ if (uport->type == PORT_UNKNOWN || !uport->ops->request_port)
+ return 0;
+
+ retval = uport->ops->request_port(uport);
+ if (retval == 0)
+ return 0; /* succeeded => done */
+
+ /*
+ * If we fail to request resources for the new port, try to restore the
+ * old settings.
+ */
+ uport->iobase = old_iobase;
+ uport->type = old_type;
+ uport->hub6 = old_hub6;
+ uport->iotype = old_iotype;
+ uport->regshift = old_shift;
+ uport->mapbase = old_mapbase;
+
+ if (old_type == PORT_UNKNOWN)
+ return retval;
+
+ retval = uport->ops->request_port(uport);
+ /* If we failed to restore the old settings, we fail like this. */
+ if (retval)
+ uport->type = PORT_UNKNOWN;
+
+ /* We failed anyway. */
+ return -EBUSY;
+}
+
static int uart_set_info(struct tty_struct *tty, struct tty_port *port,
struct uart_state *state,
struct serial_struct *new_info)
unsigned int change_irq, change_port, closing_wait;
unsigned int old_custom_divisor, close_delay;
upf_t old_flags, new_flags;
- int retval = 0;
+ int retval;
if (!uport)
return -EIO;
if (!(uport->flags & UPF_FIXED_PORT)) {
unsigned int uartclk = new_info->baud_base * 16;
/* check needs to be done here before other settings made */
- if (uartclk == 0) {
- retval = -EINVAL;
- goto exit;
- }
+ if (uartclk == 0)
+ return -EINVAL;
}
if (!capable(CAP_SYS_ADMIN)) {
- retval = -EPERM;
if (change_irq || change_port ||
(new_info->baud_base != uport->uartclk / 16) ||
(close_delay != port->close_delay) ||
(new_info->xmit_fifo_size &&
new_info->xmit_fifo_size != uport->fifosize) ||
(((new_flags ^ old_flags) & ~UPF_USR_MASK) != 0))
- goto exit;
+ return -EPERM;
uport->flags = ((uport->flags & ~UPF_USR_MASK) |
(new_flags & UPF_USR_MASK));
uport->custom_divisor = new_info->custom_divisor;
if (change_irq || change_port) {
retval = security_locked_down(LOCKDOWN_TIOCSSERIAL);
if (retval)
- goto exit;
+ return retval;
}
- /*
- * Ask the low level driver to verify the settings.
- */
- if (uport->ops->verify_port)
+ /* Ask the low level driver to verify the settings. */
+ if (uport->ops->verify_port) {
retval = uport->ops->verify_port(uport, new_info);
+ if (retval)
+ return retval;
+ }
if ((new_info->irq >= irq_get_nr_irqs()) || (new_info->irq < 0) ||
(new_info->baud_base < 9600))
- retval = -EINVAL;
-
- if (retval)
- goto exit;
+ return -EINVAL;
if (change_port || change_irq) {
- retval = -EBUSY;
-
- /*
- * Make sure that we are the sole user of this port.
- */
+ /* Make sure that we are the sole user of this port. */
if (tty_port_users(port) > 1)
- goto exit;
+ return -EBUSY;
/*
* We need to shutdown the serial port at the old
}
if (change_port) {
- unsigned long old_iobase, old_mapbase;
- unsigned int old_type, old_iotype, old_hub6, old_shift;
-
- old_iobase = uport->iobase;
- old_mapbase = uport->mapbase;
- old_type = uport->type;
- old_hub6 = uport->hub6;
- old_iotype = uport->iotype;
- old_shift = uport->regshift;
-
- /*
- * Free and release old regions
- */
- if (old_type != PORT_UNKNOWN && uport->ops->release_port)
- uport->ops->release_port(uport);
-
- uport->iobase = new_port;
- uport->type = new_info->type;
- uport->hub6 = new_info->hub6;
- uport->iotype = new_info->io_type;
- uport->regshift = new_info->iomem_reg_shift;
- uport->mapbase = (unsigned long)new_info->iomem_base;
-
- /*
- * Claim and map the new regions
- */
- if (uport->type != PORT_UNKNOWN && uport->ops->request_port) {
- retval = uport->ops->request_port(uport);
- } else {
- /* Always success - Jean II */
- retval = 0;
- }
-
- /*
- * If we fail to request resources for the
- * new port, try to restore the old settings.
- */
- if (retval) {
- uport->iobase = old_iobase;
- uport->type = old_type;
- uport->hub6 = old_hub6;
- uport->iotype = old_iotype;
- uport->regshift = old_shift;
- uport->mapbase = old_mapbase;
-
- if (old_type != PORT_UNKNOWN) {
- retval = uport->ops->request_port(uport);
- /*
- * If we failed to restore the old settings,
- * we fail like this.
- */
- if (retval)
- uport->type = PORT_UNKNOWN;
-
- /*
- * We failed anyway.
- */
- retval = -EBUSY;
- }
-
- /* Added to return the correct error -Ram Gupta */
- goto exit;
- }
+ retval = uart_change_port(uport, new_info, new_port);
+ if (retval)
+ return retval;
}
if (change_irq)
uport->fifosize = new_info->xmit_fifo_size;
check_and_exit:
- retval = 0;
if (uport->type == PORT_UNKNOWN)
- goto exit;
+ return 0;
+
if (tty_port_initialized(port)) {
if (((old_flags ^ uport->flags) & UPF_SPD_MASK) ||
old_custom_divisor != uport->custom_divisor) {
}
uart_change_line_settings(tty, state, NULL);
}
- } else {
- retval = uart_startup(tty, state, true);
- if (retval == 0)
- tty_port_set_initialized(port, true);
- if (retval > 0)
- retval = 0;
+
+ return 0;
}
- exit:
- return retval;
+
+ retval = uart_startup(tty, state, true);
+ if (retval < 0)
+ return retval;
+ if (retval == 0)
+ tty_port_set_initialized(port, true);
+
+ return 0;
}
static int uart_set_info_user(struct tty_struct *tty, struct serial_struct *ss)
struct uart_driver *driver;
};
- static int serial_match_port(struct device *dev, void *data)
+ static int serial_match_port(struct device *dev, const void *data)
{
- struct uart_match *match = data;
+ const struct uart_match *match = data;
struct tty_driver *tty_drv = match->driver->tty_driver;
dev_t devt = MKDEV(tty_drv->major, tty_drv->minor_start) +
match->port->line;
if (ret)
return ret;
- mutex_lock(&port->mutex);
+ guard(mutex)(&port->mutex);
uport = uart_port_check(state);
- if (uport) {
- oldconsole = uart_console_registered(uport);
- if (oldconsole && !newconsole) {
- ret = unregister_console(uport->cons);
- } else if (!oldconsole && newconsole) {
- if (uart_console(uport)) {
- uport->console_reinit = 1;
- register_console(uport->cons);
- } else {
- ret = -ENOENT;
- }
- }
- } else {
- ret = -ENXIO;
+ if (!uport)
+ return -ENXIO;
+
+ oldconsole = uart_console_registered(uport);
+ if (oldconsole && !newconsole) {
+ ret = unregister_console(uport->cons);
+ if (ret < 0)
+ return ret;
+ } else if (!oldconsole && newconsole) {
+ if (!uart_console(uport))
+ return -ENOENT;
+
+ uport->console_reinit = 1;
+ register_console(uport->cons);
}
- mutex_unlock(&port->mutex);
- return ret < 0 ? ret : count;
+ return count;
}
static DEVICE_ATTR_RO(uartclk);
{
struct uart_state *state;
struct tty_port *port;
- int ret = 0;
struct device *tty_dev;
int num_groups;
state = drv->state + uport->line;
port = &state->port;
- mutex_lock(&port->mutex);
- if (state->uart_port) {
- ret = -EINVAL;
- goto out;
- }
+ guard(mutex)(&port->mutex);
+ if (state->uart_port)
+ return -EINVAL;
/* Link the port to the driver state table and vice versa */
atomic_set(&state->refcount, 1);
uport->minor = drv->tty_driver->minor_start + uport->line;
uport->name = kasprintf(GFP_KERNEL, "%s%d", drv->dev_name,
drv->tty_driver->name_base + uport->line);
- if (!uport->name) {
- ret = -ENOMEM;
- goto out;
- }
+ if (!uport->name)
+ return -ENOMEM;
if (uport->cons && uport->dev)
of_console_check(uport->dev->of_node, uport->cons->name, uport->line);
uport->tty_groups = kcalloc(num_groups, sizeof(*uport->tty_groups),
GFP_KERNEL);
- if (!uport->tty_groups) {
- ret = -ENOMEM;
- goto out;
- }
+ if (!uport->tty_groups)
+ return -ENOMEM;
+
uport->tty_groups[0] = &tty_dev_attr_group;
if (uport->attr_group)
uport->tty_groups[1] = uport->attr_group;
uport->line);
}
- out:
- mutex_unlock(&port->mutex);
-
- return ret;
+ return 0;
}
/**
struct serial_ctrl_device *ctrl_dev, *new_ctrl_dev = NULL;
int ret;
- mutex_lock(&port_mutex);
+ guard(mutex)(&port_mutex);
/*
* Prevent serial_port_runtime_resume() from trying to use the port
ctrl_dev = serial_core_ctrl_find(drv, port->dev, port->ctrl_id);
if (!ctrl_dev) {
new_ctrl_dev = serial_core_ctrl_device_add(port);
- if (IS_ERR(new_ctrl_dev)) {
- ret = PTR_ERR(new_ctrl_dev);
- goto err_unlock;
- }
+ if (IS_ERR(new_ctrl_dev))
+ return PTR_ERR(new_ctrl_dev);
ctrl_dev = new_ctrl_dev;
}
if (ret)
goto err_unregister_port_dev;
- mutex_unlock(&port_mutex);
-
return 0;
err_unregister_port_dev:
err_unregister_ctrl_dev:
serial_base_ctrl_device_remove(new_ctrl_dev);
-err_unlock:
- mutex_unlock(&port_mutex);
-
return ret;
}
*/
+#include <linux/string_choices.h>
#include <linux/uaccess.h>
#include "mtu3.h"
static int mtu3_ep_open(struct inode *inode, struct file *file)
{
- const char *file_name = file_dentry(file)->d_iname;
- const struct mtu3_file_map *f_map;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(mtu3_ep_files); i++) {
- f_map = &mtu3_ep_files[i];
-
- if (strcmp(f_map->name, file_name) == 0)
- break;
- }
+ const struct mtu3_file_map *f_map = debugfs_get_aux(file);
return single_open(file, f_map->show, inode->i_private);
}
static int mtu3_probe_show(struct seq_file *sf, void *unused)
{
- const char *file_name = file_dentry(sf->file)->d_iname;
struct mtu3 *mtu = sf->private;
- const struct debugfs_reg32 *regs;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(mtu3_prb_regs); i++) {
- regs = &mtu3_prb_regs[i];
-
- if (strcmp(regs->name, file_name) == 0)
- break;
- }
+ const struct debugfs_reg32 *regs = debugfs_get_aux(sf->file);
seq_printf(sf, "0x%04x - 0x%08x\n", (u32)regs->offset,
mtu3_readl(mtu->ippc_base, (u32)regs->offset));
static ssize_t mtu3_probe_write(struct file *file, const char __user *ubuf,
size_t count, loff_t *ppos)
{
- const char *file_name = file_dentry(file)->d_iname;
struct seq_file *sf = file->private_data;
struct mtu3 *mtu = sf->private;
- const struct debugfs_reg32 *regs;
+ const struct debugfs_reg32 *regs = debugfs_get_aux(file);
char buf[32];
u32 val;
- int i;
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
return -EFAULT;
if (kstrtou32(buf, 0, &val))
return -EINVAL;
- for (i = 0; i < ARRAY_SIZE(mtu3_prb_regs); i++) {
- regs = &mtu3_prb_regs[i];
-
- if (strcmp(regs->name, file_name) == 0)
- break;
- }
mtu3_writel(mtu->ippc_base, (u32)regs->offset, val);
return count;
for (i = 0; i < ARRAY_SIZE(mtu3_prb_regs); i++) {
regs = &mtu3_prb_regs[i];
- debugfs_create_file(regs->name, 0644, dir_prb,
- mtu, &mtu3_probe_fops);
+ debugfs_create_file_aux(regs->name, 0644, dir_prb,
+ mtu, regs, &mtu3_probe_fops);
}
mtu3_debugfs_regset(mtu, mtu->ippc_base, mtu3_prb_regs,
for (i = 0; i < ARRAY_SIZE(mtu3_ep_files); i++) {
files = &mtu3_ep_files[i];
- debugfs_create_file(files->name, 0444, dir_ep,
- mep, &mtu3_ep_fops);
+ debugfs_create_file_aux(files->name, 0444, dir_ep,
+ mep, files, &mtu3_ep_fops);
}
}
struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
seq_printf(sf, "vbus state: %s\n(echo on/off)\n",
- regulator_is_enabled(otg_sx->vbus) ? "on" : "off");
+ str_on_off(regulator_is_enabled(otg_sx->vbus)));
return 0;
}
#include <linux/mutex.h>
#include <linux/property.h>
#include <linux/slab.h>
+#include <linux/string_choices.h>
#include <linux/usb/pd_vdo.h>
#include <linux/usb/typec_mux.h>
#include <linux/usb/typec_retimer.h>
/* ------------------------------------------------------------------------- */
/* Alternate Modes */
- static int altmode_match(struct device *dev, void *data)
+ static int altmode_match(struct device *dev, const void *data)
{
struct typec_altmode *adev = to_typec_altmode(dev);
- struct typec_device_id *id = data;
+ const struct typec_device_id *id = data;
if (!is_typec_altmode(dev))
return 0;
- return ((adev->svid == id->svid) && (adev->mode == id->mode));
+ return (adev->svid == id->svid);
}
static void typec_altmode_set_partner(struct altmode *altmode)
{
struct typec_altmode *adev = &altmode->adev;
- struct typec_device_id id = { adev->svid, adev->mode, };
+ struct typec_device_id id = { adev->svid };
struct typec_port *port = typec_altmode2port(adev);
struct altmode *partner;
struct device *dev;
{
struct typec_altmode *alt = to_typec_altmode(dev);
- return sprintf(buf, "%s\n", alt->active ? "yes" : "no");
+ return sprintf(buf, "%s\n", str_yes_no(alt->active));
}
static ssize_t active_store(struct device *dev, struct device_attribute *attr,
struct typec_altmode *adev = to_typec_altmode(kobj_to_dev(kobj));
if (attr == &dev_attr_active.attr)
- if (!adev->ops || !adev->ops->activate)
+ if (!is_typec_port(adev->dev.parent) &&
+ (!adev->ops || !adev->ops->activate))
return 0444;
return attr->mode;
if (is_port) {
alt->attrs[3] = &dev_attr_supported_roles.attr;
- alt->adev.active = true; /* Enabled by default */
+ alt->adev.active = !desc->inactive; /* Enabled by default */
}
sprintf(alt->group_name, "mode%d", desc->mode);
{
struct typec_partner *p = to_typec_partner(dev);
- return sprintf(buf, "%s\n", p->usb_pd ? "yes" : "no");
+ return sprintf(buf, "%s\n", str_yes_no(p->usb_pd));
}
static DEVICE_ATTR_RO(supports_usb_power_delivery);
.release = typec_cable_release,
};
- static int cable_match(struct device *dev, void *data)
- {
- return is_typec_cable(dev);
- }
-
/**
* typec_cable_get - Get a reference to the USB Type-C cable
* @port: The USB Type-C Port the cable is connected to
{
struct device *dev;
- dev = device_find_child(&port->dev, NULL, cable_match);
+ dev = device_find_child(&port->dev, &typec_cable_dev_type,
+ device_match_type);
if (!dev)
return NULL;
struct typec_port *port = to_typec_port(dev);
return sprintf(buf, "%s\n",
- port->vconn_role == TYPEC_SOURCE ? "yes" : "no");
+ str_yes_no(port->vconn_role == TYPEC_SOURCE));
}
static DEVICE_ATTR_RW(vconn_source);
/* --------------------------------------- */
/* Driver callbacks to report role updates */
- static int partner_match(struct device *dev, void *data)
- {
- return is_typec_partner(dev);
- }
-
static struct typec_partner *typec_get_partner(struct typec_port *port)
{
struct device *dev;
- dev = device_find_child(&port->dev, NULL, partner_match);
+ dev = device_find_child(&port->dev, &typec_partner_dev_type,
+ device_match_type);
if (!dev)
return NULL;
sysfs_notify(&port->dev.kobj, NULL, "power_operation_mode");
kobject_uevent(&port->dev.kobj, KOBJ_CHANGE);
- partner_dev = device_find_child(&port->dev, NULL, partner_match);
+ partner_dev = device_find_child(&port->dev,
+ &typec_partner_dev_type,
+ device_match_type);
if (partner_dev) {
struct typec_partner *partner = to_typec_partner(partner_dev);
enum usb_pd_svdm_ver svdm_version;
struct device *partner_dev;
- partner_dev = device_find_child(&port->dev, NULL, partner_match);
+ partner_dev = device_find_child(&port->dev,
+ &typec_partner_dev_type,
+ device_match_type);
if (!partner_dev)
return -ENODEV;
enum usb_pd_svdm_ver svdm_version;
struct device *cable_dev;
- cable_dev = device_find_child(&port->dev, NULL, cable_match);
+ cable_dev = device_find_child(&port->dev, &typec_cable_dev_type,
+ device_match_type);
if (!cable_dev)
return -ENODEV;
* will never get called until they do.
* @remove: Called when a device removed from this bus.
* @shutdown: Called at shut-down time to quiesce the device.
+ * @irq_get_affinity: Get IRQ affinity mask for the device on this bus.
*
* @online: Called to put the device back online (after offlining it).
* @offline: Called to put the device offline for hot-removal. May fail.
void (*sync_state)(struct device *dev);
void (*remove)(struct device *dev);
void (*shutdown)(struct device *dev);
+ const struct cpumask *(*irq_get_affinity)(struct device *dev,
+ unsigned int irq_vec);
int (*online)(struct device *dev);
int (*offline)(struct device *dev);
/* Generic device matching functions that all busses can use to match with */
int device_match_name(struct device *dev, const void *name);
+ int device_match_type(struct device *dev, const void *type);
int device_match_of_node(struct device *dev, const void *np);
int device_match_fwnode(struct device *dev, const void *fwnode);
int device_match_devt(struct device *dev, const void *pdevt);
int device_match_acpi_handle(struct device *dev, const void *handle);
int device_match_any(struct device *dev, const void *unused);
+ /* Device iterating function type for various driver core for_each APIs */
+ typedef int (*device_iter_t)(struct device *dev, void *data);
+
/* iterator helpers for buses */
- int bus_for_each_dev(const struct bus_type *bus, struct device *start, void *data,
- int (*fn)(struct device *dev, void *data));
+ int bus_for_each_dev(const struct bus_type *bus, struct device *start,
+ void *data, device_iter_t fn);
struct device *bus_find_device(const struct bus_type *bus, struct device *start,
const void *data, device_match_t match);
/**
unsigned int target_id);
extern int iscsi_session_event(struct iscsi_cls_session *session,
enum iscsi_uevent_e event);
-extern struct iscsi_cls_session *iscsi_create_session(struct Scsi_Host *shost,
- struct iscsi_transport *t,
- int dd_size,
- unsigned int target_id);
extern void iscsi_force_destroy_session(struct iscsi_cls_session *session);
extern void iscsi_remove_session(struct iscsi_cls_session *session);
extern void iscsi_free_session(struct iscsi_cls_session *session);
extern int iscsi_flashnode_bus_match(struct device *dev,
const struct device_driver *drv);
extern struct device *
- iscsi_find_flashnode_sess(struct Scsi_Host *shost, void *data,
- int (*fn)(struct device *dev, void *data));
+ iscsi_find_flashnode_sess(struct Scsi_Host *shost, const void *data,
+ device_match_t fn);
extern struct device *
iscsi_find_flashnode_conn(struct iscsi_bus_flash_session *fnode_sess);
return t == &btf_void;
}
-static bool btf_type_is_fwd(const struct btf_type *t)
-{
- return BTF_INFO_KIND(t->info) == BTF_KIND_FWD;
-}
-
static bool btf_type_is_datasec(const struct btf_type *t)
{
return BTF_INFO_KIND(t->info) == BTF_KIND_DATASEC;
struct btf *btf;
CLASS(fd, f)(fd);
- if (fd_empty(f))
- return ERR_PTR(-EBADF);
-
- if (fd_file(f)->f_op != &btf_fops)
- return ERR_PTR(-EINVAL);
-
- btf = fd_file(f)->private_data;
- refcount_inc(&btf->refcnt);
+ btf = __btf_get_by_fd(f);
+ if (!IS_ERR(btf))
+ refcount_inc(&btf->refcnt);
return btf;
}
static LIST_HEAD(btf_modules);
static DEFINE_MUTEX(btf_module_mutex);
- static ssize_t
- btf_module_read(struct file *file, struct kobject *kobj,
- struct bin_attribute *bin_attr,
- char *buf, loff_t off, size_t len)
- {
- const struct btf *btf = bin_attr->private;
-
- memcpy(buf, btf->data + off, len);
- return len;
- }
-
static void purge_cand_cache(struct btf *btf);
static int btf_module_notify(struct notifier_block *nb, unsigned long op,
attr->attr.name = btf->name;
attr->attr.mode = 0444;
attr->size = btf->data_size;
- attr->private = btf;
- attr->read = btf_module_read;
+ attr->private = btf->data;
+ attr->read_new = sysfs_bin_attr_simple_read;
err = sysfs_create_bin_file(btf_kobj, attr);
if (err) {
*/
#ifdef CONFIG_KALLSYMS
-struct module_sect_attr {
- struct bin_attribute battr;
- unsigned long address;
-};
-
struct module_sect_attrs {
struct attribute_group grp;
- unsigned int nsections;
- struct module_sect_attr attrs[];
+ struct bin_attribute attrs[];
};
#define MODULE_SECT_READ_SIZE (3 /* "0x", "\n" */ + (BITS_PER_LONG / 4))
static ssize_t module_sect_read(struct file *file, struct kobject *kobj,
- struct bin_attribute *battr,
+ const struct bin_attribute *battr,
char *buf, loff_t pos, size_t count)
{
- struct module_sect_attr *sattr =
- container_of(battr, struct module_sect_attr, battr);
char bounce[MODULE_SECT_READ_SIZE + 1];
size_t wrote;
*/
wrote = scnprintf(bounce, sizeof(bounce), "0x%px\n",
kallsyms_show_value(file->f_cred)
- ? (void *)sattr->address : NULL);
+ ? battr->private : NULL);
count = min(count, wrote);
memcpy(buf, bounce, count);
static void free_sect_attrs(struct module_sect_attrs *sect_attrs)
{
- unsigned int section;
+ const struct bin_attribute *const *bin_attr;
- for (section = 0; section < sect_attrs->nsections; section++)
- kfree(sect_attrs->attrs[section].battr.attr.name);
+ for (bin_attr = sect_attrs->grp.bin_attrs_new; *bin_attr; bin_attr++)
+ kfree((*bin_attr)->attr.name);
+ kfree(sect_attrs->grp.bin_attrs_new);
kfree(sect_attrs);
}
static int add_sect_attrs(struct module *mod, const struct load_info *info)
{
- unsigned int nloaded = 0, i, size[2];
struct module_sect_attrs *sect_attrs;
- struct module_sect_attr *sattr;
- struct bin_attribute **gattr;
+ const struct bin_attribute **gattr;
+ struct bin_attribute *sattr;
+ unsigned int nloaded = 0, i;
int ret;
/* Count loaded sections and allocate structures */
for (i = 0; i < info->hdr->e_shnum; i++)
if (!sect_empty(&info->sechdrs[i]))
nloaded++;
- size[0] = ALIGN(struct_size(sect_attrs, attrs, nloaded),
- sizeof(sect_attrs->grp.bin_attrs[0]));
- size[1] = (nloaded + 1) * sizeof(sect_attrs->grp.bin_attrs[0]);
- sect_attrs = kzalloc(size[0] + size[1], GFP_KERNEL);
+ sect_attrs = kzalloc(struct_size(sect_attrs, attrs, nloaded), GFP_KERNEL);
if (!sect_attrs)
return -ENOMEM;
+ gattr = kcalloc(nloaded + 1, sizeof(*gattr), GFP_KERNEL);
+ if (!gattr) {
+ kfree(sect_attrs);
+ return -ENOMEM;
+ }
+
/* Setup section attributes. */
sect_attrs->grp.name = "sections";
- sect_attrs->grp.bin_attrs = (void *)sect_attrs + size[0];
+ sect_attrs->grp.bin_attrs_new = gattr;
- sect_attrs->nsections = 0;
sattr = §_attrs->attrs[0];
- gattr = §_attrs->grp.bin_attrs[0];
for (i = 0; i < info->hdr->e_shnum; i++) {
Elf_Shdr *sec = &info->sechdrs[i];
if (sect_empty(sec))
continue;
- sysfs_bin_attr_init(&sattr->battr);
- sattr->address = sec->sh_addr;
- sattr->battr.attr.name =
+ sysfs_bin_attr_init(sattr);
+ sattr->attr.name =
kstrdup(info->secstrings + sec->sh_name, GFP_KERNEL);
- if (!sattr->battr.attr.name) {
+ if (!sattr->attr.name) {
ret = -ENOMEM;
goto out;
}
- sect_attrs->nsections++;
- sattr->battr.read = module_sect_read;
- sattr->battr.size = MODULE_SECT_READ_SIZE;
- sattr->battr.attr.mode = 0400;
- *(gattr++) = &(sattr++)->battr;
+ sattr->read_new = module_sect_read;
+ sattr->private = (void *)sec->sh_addr;
+ sattr->size = MODULE_SECT_READ_SIZE;
+ sattr->attr.mode = 0400;
+ *(gattr++) = sattr++;
}
- *gattr = NULL;
ret = sysfs_create_group(&mod->mkobj.kobj, §_attrs->grp);
if (ret)
*/
struct module_notes_attrs {
- struct kobject *dir;
- unsigned int notes;
- struct bin_attribute attrs[] __counted_by(notes);
+ struct attribute_group grp;
+ struct bin_attribute attrs[];
};
-static void free_notes_attrs(struct module_notes_attrs *notes_attrs,
- unsigned int i)
+static void free_notes_attrs(struct module_notes_attrs *notes_attrs)
{
- if (notes_attrs->dir) {
- while (i-- > 0)
- sysfs_remove_bin_file(notes_attrs->dir,
- ¬es_attrs->attrs[i]);
- kobject_put(notes_attrs->dir);
- }
+ kfree(notes_attrs->grp.bin_attrs_new);
kfree(notes_attrs);
}
{
unsigned int notes, loaded, i;
struct module_notes_attrs *notes_attrs;
+ const struct bin_attribute **gattr;
struct bin_attribute *nattr;
int ret;
if (!notes_attrs)
return -ENOMEM;
- notes_attrs->notes = notes;
+ gattr = kcalloc(notes + 1, sizeof(*gattr), GFP_KERNEL);
+ if (!gattr) {
+ kfree(notes_attrs);
+ return -ENOMEM;
+ }
+
+ notes_attrs->grp.name = "notes";
+ notes_attrs->grp.bin_attrs_new = gattr;
+
nattr = ¬es_attrs->attrs[0];
for (loaded = i = 0; i < info->hdr->e_shnum; ++i) {
if (sect_empty(&info->sechdrs[i]))
continue;
if (info->sechdrs[i].sh_type == SHT_NOTE) {
sysfs_bin_attr_init(nattr);
- nattr->attr.name = mod->sect_attrs->attrs[loaded].battr.attr.name;
+ nattr->attr.name = mod->sect_attrs->attrs[loaded].attr.name;
nattr->attr.mode = 0444;
nattr->size = info->sechdrs[i].sh_size;
nattr->private = (void *)info->sechdrs[i].sh_addr;
- nattr->read = sysfs_bin_attr_simple_read;
+ nattr->read_new = sysfs_bin_attr_simple_read;
- ++nattr;
+ *(gattr++) = nattr++;
}
++loaded;
}
- notes_attrs->dir = kobject_create_and_add("notes", &mod->mkobj.kobj);
- if (!notes_attrs->dir) {
- ret = -ENOMEM;
+ ret = sysfs_create_group(&mod->mkobj.kobj, ¬es_attrs->grp);
+ if (ret)
goto out;
- }
-
- for (i = 0; i < notes; ++i) {
- ret = sysfs_create_bin_file(notes_attrs->dir, ¬es_attrs->attrs[i]);
- if (ret)
- goto out;
- }
mod->notes_attrs = notes_attrs;
return 0;
out:
- free_notes_attrs(notes_attrs, i);
+ free_notes_attrs(notes_attrs);
return ret;
}
static void remove_notes_attrs(struct module *mod)
{
- if (mod->notes_attrs)
- free_notes_attrs(mod->notes_attrs, mod->notes_attrs->notes);
+ if (mod->notes_attrs) {
+ sysfs_remove_group(&mod->mkobj.kobj,
+ &mod->notes_attrs->grp);
+ /*
+ * We are positive that no one is using any notes attrs
+ * at this point. Deallocate immediately.
+ */
+ free_notes_attrs(mod->notes_attrs);
+ mod->notes_attrs = NULL;
+ }
}
#else /* !CONFIG_KALLSYMS */
static void module_remove_modinfo_attrs(struct module *mod, int end)
{
- struct module_attribute *attr;
+ const struct module_attribute *attr;
int i;
for (i = 0; (attr = &mod->modinfo_attrs[i]); i++) {
static int module_add_modinfo_attrs(struct module *mod)
{
- struct module_attribute *attr;
+ const struct module_attribute *attr;
struct module_attribute *temp_attr;
int error = 0;
int i;
* We have to do this manually because the rcu_head is
* not located inside the object.
*/
- kasan_record_aux_stack_noalloc(x);
+ kasan_record_aux_stack(x);
delayed_free->object = x;
call_rcu(&delayed_free->head, slab_free_after_rcu_debug);
unsigned int order = oo_order(oo);
if (node == NUMA_NO_NODE)
- folio = (struct folio *)alloc_pages(flags, order);
+ folio = (struct folio *)alloc_frozen_pages(flags, order);
else
- folio = (struct folio *)__alloc_pages_node(node, flags, order);
+ folio = (struct folio *)__alloc_frozen_pages(flags, order, node, NULL);
if (!folio)
return NULL;
slab = folio_slab(folio);
__folio_set_slab(folio);
- /* Make the flag visible before any changes to folio->mapping */
- smp_wmb();
if (folio_is_pfmemalloc(folio))
slab_set_pfmemalloc(slab);
__slab_clear_pfmemalloc(slab);
folio->mapping = NULL;
- /* Make the mapping reset visible before clearing the flag */
- smp_wmb();
__folio_clear_slab(folio);
mm_account_reclaimed_pages(pages);
unaccount_slab(slab, order, s);
- __free_pages(&folio->page, order);
+ free_frozen_pages(&folio->page, order);
}
static void rcu_free_slab(struct rcu_head *h)
return -ENOMEM;
}
- if (strcmp(filep->f_path.dentry->d_name.name, "alloc_traces") == 0)
- alloc = TRACK_ALLOC;
- else
- alloc = TRACK_FREE;
+ alloc = debugfs_get_aux_num(filep);
if (!alloc_loc_track(t, PAGE_SIZE / sizeof(struct location), GFP_KERNEL)) {
bitmap_free(obj_map);
slab_cache_dir = debugfs_create_dir(s->name, slab_debugfs_root);
- debugfs_create_file("alloc_traces", 0400,
- slab_cache_dir, s, &slab_debugfs_fops);
+ debugfs_create_file_aux_num("alloc_traces", 0400, slab_cache_dir, s,
+ TRACK_ALLOC, &slab_debugfs_fops);
- debugfs_create_file("free_traces", 0400,
- slab_cache_dir, s, &slab_debugfs_fops);
+ debugfs_create_file_aux_num("free_traces", 0400, slab_cache_dir, s,
+ TRACK_FREE, &slab_debugfs_fops);
}
void debugfs_slab_release(struct kmem_cache *s)
{
u16 active_links;
- if (kstrtou16(buf, 0, &active_links))
+ if (kstrtou16(buf, 0, &active_links) || !active_links)
return -EINVAL;
return ieee80211_set_active_links(&sdata->vif, active_links) ?: buflen;
void ieee80211_debugfs_rename_netdev(struct ieee80211_sub_if_data *sdata)
{
- struct dentry *dir;
- char buf[10 + IFNAMSIZ];
-
- dir = sdata->vif.debugfs_dir;
-
- if (IS_ERR_OR_NULL(dir))
- return;
-
- sprintf(buf, "netdev:%s", sdata->name);
- debugfs_rename(dir->d_parent, dir, dir->d_parent, buf);
+ debugfs_change_name(sdata->vif.debugfs_dir, "netdev:%s", sdata->name);
}
void ieee80211_debugfs_recreate_netdev(struct ieee80211_sub_if_data *sdata,
if (result)
return result;
- if (!IS_ERR_OR_NULL(rdev->wiphy.debugfsdir))
- debugfs_rename(rdev->wiphy.debugfsdir->d_parent,
- rdev->wiphy.debugfsdir,
- rdev->wiphy.debugfsdir->d_parent, newname);
+ debugfs_change_name(rdev->wiphy.debugfsdir, "%s", newname);
nl80211_notify_wiphy(rdev, NL80211_CMD_NEW_WIPHY);
return err;
}
- wiphy_lock(&rdev->wiphy);
+ guard(wiphy)(&rdev->wiphy);
+
list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
if (!wdev->netdev)
continue;
continue;
nl80211_notify_iface(rdev, wdev, NL80211_CMD_NEW_INTERFACE);
}
- wiphy_unlock(&rdev->wiphy);
return 0;
}
{
struct cfg80211_registered_device *rdev = data;
- wiphy_lock(&rdev->wiphy);
+ guard(wiphy)(&rdev->wiphy);
+
rdev_rfkill_poll(rdev);
- wiphy_unlock(&rdev->wiphy);
}
void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev,
/* otherwise, check iftype */
- wiphy_lock(wiphy);
+ guard(wiphy)(wiphy);
switch (wdev->iftype) {
case NL80211_IFTYPE_P2P_DEVICE:
default:
break;
}
-
- wiphy_unlock(wiphy);
}
}
EXPORT_SYMBOL_GPL(cfg80211_shutdown_all_interfaces);
rdev = container_of(work, struct cfg80211_registered_device,
event_work);
- wiphy_lock(&rdev->wiphy);
+ guard(wiphy)(&rdev->wiphy);
+
cfg80211_process_rdev_events(rdev);
- wiphy_unlock(&rdev->wiphy);
}
void cfg80211_destroy_ifaces(struct cfg80211_registered_device *rdev)
if (wdev->netdev)
dev_close(wdev->netdev);
- wiphy_lock(&rdev->wiphy);
+ guard(wiphy)(&rdev->wiphy);
+
cfg80211_leave(rdev, wdev);
cfg80211_remove_virtual_intf(rdev, wdev);
- wiphy_unlock(&rdev->wiphy);
}
}
}
trace_wiphy_work_worker_start(&rdev->wiphy);
- wiphy_lock(&rdev->wiphy);
+ guard(wiphy)(&rdev->wiphy);
if (rdev->suspended)
- goto out;
+ return;
spin_lock_irq(&rdev->wiphy_work_lock);
wk = list_first_entry_or_null(&rdev->wiphy_work_list,
} else {
spin_unlock_irq(&rdev->wiphy_work_lock);
}
-out:
- wiphy_unlock(&rdev->wiphy);
}
/* exported functions */
break;
case NETDEV_REGISTER:
if (!wdev->registered) {
- wiphy_lock(&rdev->wiphy);
+ guard(wiphy)(&rdev->wiphy);
+
cfg80211_register_wdev(rdev, wdev);
- wiphy_unlock(&rdev->wiphy);
}
break;
case NETDEV_UNREGISTER:
* so check wdev->registered.
*/
if (wdev->registered && !wdev->registering) {
- wiphy_lock(&rdev->wiphy);
+ guard(wiphy)(&rdev->wiphy);
+
_cfg80211_unregister_wdev(wdev, false);
- wiphy_unlock(&rdev->wiphy);
}
break;
case NETDEV_GOING_DOWN:
- wiphy_lock(&rdev->wiphy);
- cfg80211_leave(rdev, wdev);
- cfg80211_remove_links(wdev);
- wiphy_unlock(&rdev->wiphy);
+ scoped_guard(wiphy, &rdev->wiphy) {
+ cfg80211_leave(rdev, wdev);
+ cfg80211_remove_links(wdev);
+ }
/* since we just did cfg80211_leave() nothing to do there */
cancel_work_sync(&wdev->disconnect_wk);
cancel_work_sync(&wdev->pmsr_free_wk);
use crate::{
bindings,
+ str::CStr,
types::{ARef, Opaque},
};
use core::{fmt, ptr};
#[cfg(CONFIG_PRINTK)]
unsafe {
bindings::_dev_printk(
- klevel as *const _ as *const core::ffi::c_char,
+ klevel as *const _ as *const crate::ffi::c_char,
self.as_raw(),
c_str!("%pA").as_char_ptr(),
- &msg as *const _ as *const core::ffi::c_void,
+ &msg as *const _ as *const crate::ffi::c_void,
)
};
}
+
+ /// Checks if property is present or not.
+ pub fn property_present(&self, name: &CStr) -> bool {
+ // SAFETY: By the invariant of `CStr`, `name` is null-terminated.
+ unsafe { bindings::device_property_present(self.as_raw().cast_const(), name.as_char_ptr()) }
+ }
}
// SAFETY: Instances of `Device` are always reference-counted.
#![no_std]
#![feature(arbitrary_self_types)]
-#![feature(coerce_unsized)]
-#![feature(dispatch_from_dyn)]
+#![cfg_attr(CONFIG_RUSTC_HAS_COERCE_POINTEE, feature(derive_coerce_pointee))]
+#![cfg_attr(not(CONFIG_RUSTC_HAS_COERCE_POINTEE), feature(coerce_unsized))]
+#![cfg_attr(not(CONFIG_RUSTC_HAS_COERCE_POINTEE), feature(dispatch_from_dyn))]
+#![cfg_attr(not(CONFIG_RUSTC_HAS_COERCE_POINTEE), feature(unsize))]
#![feature(inline_const)]
#![feature(lint_reasons)]
-#![feature(unsize)]
+ // Stable in Rust 1.83
+ #![feature(const_maybe_uninit_as_mut_ptr)]
+ #![feature(const_mut_refs)]
+ #![feature(const_ptr_write)]
+ #![feature(const_refs_to_cell)]
// Ensure conditional compilation based on the kernel configuration works;
// otherwise we may silently break things like initcall handling.
pub mod alloc;
#[cfg(CONFIG_BLOCK)]
pub mod block;
-mod build_assert;
+#[doc(hidden)]
+pub mod build_assert;
pub mod cred;
pub mod device;
+ pub mod device_id;
+ pub mod devres;
+ pub mod driver;
pub mod error;
#[cfg(CONFIG_RUST_FW_LOADER_ABSTRACTIONS)]
pub mod firmware;
pub mod fs;
pub mod init;
+ pub mod io;
pub mod ioctl;
pub mod jump_label;
#[cfg(CONFIG_KUNIT)]
pub mod miscdevice;
#[cfg(CONFIG_NET)]
pub mod net;
+ pub mod of;
pub mod page;
+ #[cfg(CONFIG_PCI)]
+ pub mod pci;
pub mod pid_namespace;
+ pub mod platform;
pub mod prelude;
pub mod print;
pub mod rbtree;
+ pub mod revocable;
pub mod security;
pub mod seq_file;
pub mod sizes;
pub use macros;
pub use uapi;
-#[doc(hidden)]
-pub use build_error::build_error;
-
/// Prefix to appear before log messages printed from within the `kernel` crate.
const __LOG_PREFIX: &[u8] = b"rust_kernel\0";
}
}
+ /// Metadata attached to a [`Module`] or [`InPlaceModule`].
+ pub trait ModuleMetadata {
+ /// The name of the module as specified in the `module!` macro.
+ const NAME: &'static crate::str::CStr;
+ }
+
/// Equivalent to `THIS_MODULE` in the C API.
///
/// C header: [`include/linux/init.h`](srctree/include/linux/init.h)
use crate::{
bindings,
+ device::Device,
error::{to_result, Error, Result, VTABLE_DEFAULT_ERROR},
+ ffi::{c_int, c_long, c_uint, c_ulong},
+ fs::File,
prelude::*,
+ seq_file::SeqFile,
str::CStr,
types::{ForeignOwnable, Opaque},
};
-use core::{
- ffi::{c_int, c_long, c_uint, c_ulong},
- marker::PhantomData,
- mem::MaybeUninit,
- pin::Pin,
-};
+use core::{marker::PhantomData, mem::MaybeUninit, pin::Pin};
/// Options for creating a misc device.
#[derive(Copy, Clone)]
pub fn as_raw(&self) -> *mut bindings::miscdevice {
self.inner.get()
}
+
+ /// Access the `this_device` field.
+ pub fn device(&self) -> &Device {
+ // SAFETY: This can only be called after a successful register(), which always
+ // initialises `this_device` with a valid device. Furthermore, the signature of this
+ // function tells the borrow-checker that the `&Device` reference must not outlive the
+ // `&MiscDeviceRegistration<T>` used to obtain it, so the last use of the reference must be
+ // before the underlying `struct miscdevice` is destroyed.
+ unsafe { Device::as_ref((*self.as_raw()).this_device) }
+ }
}
#[pinned_drop]
/// Trait implemented by the private data of an open misc device.
#[vtable]
- pub trait MiscDevice {
+ pub trait MiscDevice: Sized {
/// What kind of pointer should `Self` be wrapped in.
type Ptr: ForeignOwnable + Send + Sync;
/// Called when the misc device is opened.
///
/// The returned pointer will be stored as the private data for the file.
- fn open() -> Result<Self::Ptr>;
+ fn open(_file: &File, _misc: &MiscDeviceRegistration<Self>) -> Result<Self::Ptr>;
/// Called when the misc device is released.
- fn release(device: Self::Ptr) {
+ fn release(device: Self::Ptr, _file: &File) {
drop(device);
}
/// [`kernel::ioctl`]: mod@crate::ioctl
fn ioctl(
_device: <Self::Ptr as ForeignOwnable>::Borrowed<'_>,
+ _file: &File,
_cmd: u32,
_arg: usize,
) -> Result<isize> {
- kernel::build_error!(VTABLE_DEFAULT_ERROR)
+ build_error!(VTABLE_DEFAULT_ERROR)
}
/// Handler for ioctls.
#[cfg(CONFIG_COMPAT)]
fn compat_ioctl(
_device: <Self::Ptr as ForeignOwnable>::Borrowed<'_>,
+ _file: &File,
_cmd: u32,
_arg: usize,
) -> Result<isize> {
- kernel::build_error!(VTABLE_DEFAULT_ERROR)
+ build_error!(VTABLE_DEFAULT_ERROR)
}
- kernel::build_error!(VTABLE_DEFAULT_ERROR)
+
+ /// Show info for this fd.
+ fn show_fdinfo(
+ _device: <Self::Ptr as ForeignOwnable>::Borrowed<'_>,
+ _m: &SeqFile,
+ _file: &File,
+ ) {
++ build_error!(VTABLE_DEFAULT_ERROR)
+ }
}
const fn create_vtable<T: MiscDevice>() -> &'static bindings::file_operations {
} else {
None
},
+ show_fdinfo: maybe_fn(T::HAS_SHOW_FDINFO, fops_show_fdinfo::<T>),
// SAFETY: All zeros is a valid value for `bindings::file_operations`.
..unsafe { MaybeUninit::zeroed().assume_init() }
};
/// The file must be associated with a `MiscDeviceRegistration<T>`.
unsafe extern "C" fn fops_open<T: MiscDevice>(
inode: *mut bindings::inode,
- file: *mut bindings::file,
+ raw_file: *mut bindings::file,
) -> c_int {
// SAFETY: The pointers are valid and for a file being opened.
- let ret = unsafe { bindings::generic_file_open(inode, file) };
+ let ret = unsafe { bindings::generic_file_open(inode, raw_file) };
if ret != 0 {
return ret;
}
- let ptr = match T::open() {
+ // SAFETY: The open call of a file can access the private data.
+ let misc_ptr = unsafe { (*raw_file).private_data };
+
+ // SAFETY: This is a miscdevice, so `misc_open()` set the private data to a pointer to the
+ // associated `struct miscdevice` before calling into this method. Furthermore, `misc_open()`
+ // ensures that the miscdevice can't be unregistered and freed during this call to `fops_open`.
+ let misc = unsafe { &*misc_ptr.cast::<MiscDeviceRegistration<T>>() };
+
+ // SAFETY:
+ // * This underlying file is valid for (much longer than) the duration of `T::open`.
+ // * There is no active fdget_pos region on the file on this thread.
+ let file = unsafe { File::from_raw_file(raw_file) };
+
+ let ptr = match T::open(file, misc) {
Ok(ptr) => ptr,
Err(err) => return err.to_errno(),
};
- // SAFETY: The open call of a file owns the private data.
- unsafe { (*file).private_data = ptr.into_foreign() };
+ // This overwrites the private data with the value specified by the user, changing the type of
+ // this file's private data. All future accesses to the private data is performed by other
+ // fops_* methods in this file, which all correctly cast the private data to the new type.
+ //
+ // SAFETY: The open call of a file can access the private data.
- unsafe { (*raw_file).private_data = ptr.into_foreign().cast_mut() };
++ unsafe { (*raw_file).private_data = ptr.into_foreign() };
0
}
// SAFETY: The release call of a file owns the private data.
let ptr = unsafe { <T::Ptr as ForeignOwnable>::from_foreign(private) };
- T::release(ptr);
+ // SAFETY:
+ // * The file is valid for the duration of this call.
+ // * There is no active fdget_pos region on the file on this thread.
+ T::release(ptr, unsafe { File::from_raw_file(file) });
0
}
// SAFETY: Ioctl calls can borrow the private data of the file.
let device = unsafe { <T::Ptr as ForeignOwnable>::borrow(private) };
- match T::ioctl(device, cmd, arg) {
+ // SAFETY:
+ // * The file is valid for the duration of this call.
+ // * There is no active fdget_pos region on the file on this thread.
+ let file = unsafe { File::from_raw_file(file) };
+
- match T::ioctl(device, file, cmd, arg as usize) {
++ match T::ioctl(device, file, cmd, arg) {
Ok(ret) => ret as c_long,
Err(err) => err.to_errno() as c_long,
}
// SAFETY: Ioctl calls can borrow the private data of the file.
let device = unsafe { <T::Ptr as ForeignOwnable>::borrow(private) };
- match T::compat_ioctl(device, cmd, arg) {
+ // SAFETY:
+ // * The file is valid for the duration of this call.
+ // * There is no active fdget_pos region on the file on this thread.
+ let file = unsafe { File::from_raw_file(file) };
+
- match T::compat_ioctl(device, file, cmd, arg as usize) {
++ match T::compat_ioctl(device, file, cmd, arg) {
Ok(ret) => ret as c_long,
Err(err) => err.to_errno() as c_long,
}
}
+
+ /// # Safety
+ ///
+ /// - `file` must be a valid file that is associated with a `MiscDeviceRegistration<T>`.
+ /// - `seq_file` must be a valid `struct seq_file` that we can write to.
+ unsafe extern "C" fn fops_show_fdinfo<T: MiscDevice>(
+ seq_file: *mut bindings::seq_file,
+ file: *mut bindings::file,
+ ) {
+ // SAFETY: The release call of a file owns the private data.
+ let private = unsafe { (*file).private_data };
+ // SAFETY: Ioctl calls can borrow the private data of the file.
+ let device = unsafe { <T::Ptr as ForeignOwnable>::borrow(private) };
+ // SAFETY:
+ // * The file is valid for the duration of this call.
+ // * There is no active fdget_pos region on the file on this thread.
+ let file = unsafe { File::from_raw_file(file) };
+ // SAFETY: The caller ensures that the pointer is valid and exclusive for the duration in which
+ // this method is called.
+ let m = unsafe { SeqFile::from_raw(seq_file) };
+
+ T::show_fdinfo(device, m, file);
+ }
pub mod lock;
mod locked_by;
pub mod poll;
+ pub mod rcu;
pub use arc::{Arc, ArcBorrow, UniqueArc};
pub use condvar::{new_condvar, CondVar, CondVarTimeoutResult};
pub use lock::global::{global_lock, GlobalGuard, GlobalLock, GlobalLockBackend, GlobalLockedBy};
-pub use lock::mutex::{new_mutex, Mutex};
-pub use lock::spinlock::{new_spinlock, SpinLock};
+pub use lock::mutex::{new_mutex, Mutex, MutexGuard};
+pub use lock::spinlock::{new_spinlock, SpinLock, SpinLockGuard};
pub use locked_by::LockedBy;
/// Represents a lockdep class. It's a wrapper around C's `lock_class_key`.
/// This trait is meant to be used in cases when Rust objects are stored in C objects and
/// eventually "freed" back to Rust.
pub trait ForeignOwnable: Sized {
- /// Type of values borrowed between calls to [`ForeignOwnable::into_foreign`] and
- /// [`ForeignOwnable::from_foreign`].
+ /// Type used to immutably borrow a value that is currently foreign-owned.
type Borrowed<'a>;
+ /// Type used to mutably borrow a value that is currently foreign-owned.
+ type BorrowedMut<'a>;
+
/// Converts a Rust-owned object to a foreign-owned one.
///
/// The foreign representation is a pointer to void. There are no guarantees for this pointer.
/// For example, it might be invalid, dangling or pointing to uninitialized memory. Using it in
- /// any way except for [`ForeignOwnable::from_foreign`], [`ForeignOwnable::borrow`],
- /// [`ForeignOwnable::try_from_foreign`] can result in undefined behavior.
- fn into_foreign(self) -> *const crate::ffi::c_void;
-
- /// Borrows a foreign-owned object.
- ///
- /// # Safety
+ /// any way except for [`from_foreign`], [`try_from_foreign`], [`borrow`], or [`borrow_mut`] can
+ /// result in undefined behavior.
///
- /// `ptr` must have been returned by a previous call to [`ForeignOwnable::into_foreign`] for
- /// which a previous matching [`ForeignOwnable::from_foreign`] hasn't been called yet.
- unsafe fn borrow<'a>(ptr: *const crate::ffi::c_void) -> Self::Borrowed<'a>;
+ /// [`from_foreign`]: Self::from_foreign
+ /// [`try_from_foreign`]: Self::try_from_foreign
+ /// [`borrow`]: Self::borrow
+ /// [`borrow_mut`]: Self::borrow_mut
+ fn into_foreign(self) -> *mut crate::ffi::c_void;
/// Converts a foreign-owned object back to a Rust-owned one.
///
/// # Safety
///
- /// `ptr` must have been returned by a previous call to [`ForeignOwnable::into_foreign`] for
- /// which a previous matching [`ForeignOwnable::from_foreign`] hasn't been called yet.
- /// Additionally, all instances (if any) of values returned by [`ForeignOwnable::borrow`] for
- /// this object must have been dropped.
- unsafe fn from_foreign(ptr: *const crate::ffi::c_void) -> Self;
+ /// The provided pointer must have been returned by a previous call to [`into_foreign`], and it
+ /// must not be passed to `from_foreign` more than once.
+ ///
+ /// [`into_foreign`]: Self::into_foreign
+ unsafe fn from_foreign(ptr: *mut crate::ffi::c_void) -> Self;
/// Tries to convert a foreign-owned object back to a Rust-owned one.
///
///
/// # Safety
///
- /// `ptr` must either be null or satisfy the safety requirements for
- /// [`ForeignOwnable::from_foreign`].
- unsafe fn try_from_foreign(ptr: *const crate::ffi::c_void) -> Option<Self> {
+ /// `ptr` must either be null or satisfy the safety requirements for [`from_foreign`].
+ ///
+ /// [`from_foreign`]: Self::from_foreign
+ unsafe fn try_from_foreign(ptr: *mut crate::ffi::c_void) -> Option<Self> {
if ptr.is_null() {
None
} else {
unsafe { Some(Self::from_foreign(ptr)) }
}
}
+
+ /// Borrows a foreign-owned object immutably.
+ ///
+ /// This method provides a way to access a foreign-owned value from Rust immutably. It provides
+ /// you with exactly the same abilities as an `&Self` when the value is Rust-owned.
+ ///
+ /// # Safety
+ ///
+ /// The provided pointer must have been returned by a previous call to [`into_foreign`], and if
+ /// the pointer is ever passed to [`from_foreign`], then that call must happen after the end of
+ /// the lifetime 'a.
+ ///
+ /// [`into_foreign`]: Self::into_foreign
+ /// [`from_foreign`]: Self::from_foreign
+ unsafe fn borrow<'a>(ptr: *mut crate::ffi::c_void) -> Self::Borrowed<'a>;
+
+ /// Borrows a foreign-owned object mutably.
+ ///
+ /// This method provides a way to access a foreign-owned value from Rust mutably. It provides
+ /// you with exactly the same abilities as an `&mut Self` when the value is Rust-owned, except
+ /// that the address of the object must not be changed.
+ ///
+ /// Note that for types like [`Arc`], an `&mut Arc<T>` only gives you immutable access to the
+ /// inner value, so this method also only provides immutable access in that case.
+ ///
+ /// In the case of `Box<T>`, this method gives you the ability to modify the inner `T`, but it
+ /// does not let you change the box itself. That is, you cannot change which allocation the box
+ /// points at.
+ ///
+ /// # Safety
+ ///
+ /// The provided pointer must have been returned by a previous call to [`into_foreign`], and if
+ /// the pointer is ever passed to [`from_foreign`], then that call must happen after the end of
+ /// the lifetime 'a.
+ ///
+ /// The lifetime 'a must not overlap with the lifetime of any other call to [`borrow`] or
+ /// `borrow_mut` on the same object.
+ ///
+ /// [`into_foreign`]: Self::into_foreign
+ /// [`from_foreign`]: Self::from_foreign
+ /// [`borrow`]: Self::borrow
+ /// [`Arc`]: crate::sync::Arc
+ unsafe fn borrow_mut<'a>(ptr: *mut crate::ffi::c_void) -> Self::BorrowedMut<'a>;
}
impl ForeignOwnable for () {
type Borrowed<'a> = ();
+ type BorrowedMut<'a> = ();
- fn into_foreign(self) -> *const crate::ffi::c_void {
+ fn into_foreign(self) -> *mut crate::ffi::c_void {
core::ptr::NonNull::dangling().as_ptr()
}
- unsafe fn borrow<'a>(_: *const crate::ffi::c_void) -> Self::Borrowed<'a> {}
+ unsafe fn from_foreign(_: *mut crate::ffi::c_void) -> Self {}
- unsafe fn from_foreign(_: *const crate::ffi::c_void) -> Self {}
+ unsafe fn borrow<'a>(_: *mut crate::ffi::c_void) -> Self::Borrowed<'a> {}
+ unsafe fn borrow_mut<'a>(_: *mut crate::ffi::c_void) -> Self::BorrowedMut<'a> {}
}
/// Runs a cleanup function/closure when dropped.
}
}
+ /// Create an opaque pin-initializer from the given pin-initializer.
+ pub fn pin_init(slot: impl PinInit<T>) -> impl PinInit<Self> {
+ Self::ffi_init(|ptr: *mut T| {
+ // SAFETY:
+ // - `ptr` is a valid pointer to uninitialized memory,
+ // - `slot` is not accessed on error; the call is infallible,
+ // - `slot` is pinned in memory.
+ let _ = unsafe { init::PinInit::<T>::__pinned_init(slot, ptr) };
+ })
+ }
+
/// Creates a pin-initializer from the given initializer closure.
///
/// The returned initializer calls the given closure with the pointer to the inner `T` of this
/// }
///
/// let mut data = Empty {};
- /// let ptr = NonNull::<Empty>::new(&mut data as *mut _).unwrap();
+ /// let ptr = NonNull::<Empty>::new(&mut data).unwrap();
/// # // SAFETY: TODO.
/// let data_ref: ARef<Empty> = unsafe { ARef::from_raw(ptr) };
/// let raw_ptr: NonNull<Empty> = ARef::into_raw(data_ref);